Step 304: Serialization + dispatch for 9 new AST node types — Python + C++ generators (12/12 tests)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1817,4 +1817,8 @@ add_executable(step303_test tests/step303_test.cpp)
|
||||
target_include_directories(step303_test PRIVATE src)
|
||||
target_link_libraries(step303_test PRIVATE nlohmann_json::nlohmann_json)
|
||||
|
||||
add_executable(step304_test tests/step304_test.cpp)
|
||||
target_include_directories(step304_test PRIVATE src)
|
||||
target_link_libraries(step304_test PRIVATE nlohmann_json::nlohmann_json)
|
||||
|
||||
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)
|
||||
|
||||
@@ -103,6 +103,130 @@
|
||||
return type->typeName;
|
||||
}
|
||||
|
||||
// --- New AST node visitors (Step 304) ---
|
||||
|
||||
std::string visitClassDeclaration(const ASTNode* node) override {
|
||||
auto* cls = static_cast<const ClassDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = cls->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
oss << "class " << cls->name;
|
||||
if (!cls->superClass.empty()) oss << " : public " << cls->superClass;
|
||||
oss << " {\npublic:\n";
|
||||
auto fields = cls->getChildren("fields");
|
||||
for (const auto* f : fields)
|
||||
oss << " " << generate(f) << ";\n";
|
||||
auto methods = cls->getChildren("methods");
|
||||
for (const auto* m : methods) oss << generate(m);
|
||||
oss << "};\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitInterfaceDeclaration(const ASTNode* node) override {
|
||||
auto* iface = static_cast<const InterfaceDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "class " << iface->name << " {\npublic:\n";
|
||||
auto methods = iface->getChildren("methods");
|
||||
for (const auto* m : methods) oss << generate(m);
|
||||
oss << "};\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitMethodDeclaration(const ASTNode* node) override {
|
||||
auto* meth = static_cast<const MethodDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = meth->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << " " << generate(a) << "\n";
|
||||
oss << " ";
|
||||
if (meth->isStatic) oss << "static ";
|
||||
if (meth->isVirtual) oss << "virtual ";
|
||||
oss << "void " << meth->name << "(";
|
||||
auto params = meth->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << visitParameter(static_cast<const Parameter*>(params[i]));
|
||||
}
|
||||
oss << ")";
|
||||
if (meth->isOverride) oss << " override";
|
||||
auto body = meth->getChildren("body");
|
||||
if (body.empty()) {
|
||||
oss << " {}\n";
|
||||
} else {
|
||||
oss << " {\n";
|
||||
for (const auto* s : body) oss << " " << generate(s) << "\n";
|
||||
oss << " }\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitGenericType(const ASTNode* node) override {
|
||||
auto* gen = static_cast<const GenericType*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << gen->baseName << "<";
|
||||
auto params = gen->getChildren("typeParameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << ">";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitTypeParameter(const ASTNode* node) override {
|
||||
auto* tp = static_cast<const TypeParameter*>(node);
|
||||
return tp->name;
|
||||
}
|
||||
|
||||
std::string visitAsyncFunction(const ASTNode* node) override {
|
||||
auto* af = static_cast<const AsyncFunction*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = af->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
oss << "std::future<void> " << af->name << "(";
|
||||
auto params = af->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";
|
||||
auto body = af->getChildren("body");
|
||||
for (const auto* s : body) oss << " " << generate(s) << "\n";
|
||||
oss << "}\n";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitAwaitExpression(const ASTNode* node) override {
|
||||
auto* aw = static_cast<const AwaitExpression*>(node);
|
||||
auto* expr = aw->getChild("expression");
|
||||
return "co_await " + (expr ? generate(expr) : "/* missing */");
|
||||
}
|
||||
|
||||
std::string visitLambdaExpression(const ASTNode* node) override {
|
||||
auto* lam = static_cast<const LambdaExpression*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "[";
|
||||
for (size_t i = 0; i < lam->captureList.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << lam->captureList[i];
|
||||
}
|
||||
oss << "](";
|
||||
auto params = lam->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << "auto " << static_cast<const Parameter*>(params[i])->name;
|
||||
}
|
||||
oss << ") { ";
|
||||
auto body = lam->getChildren("body");
|
||||
for (const auto* s : body) oss << generate(s) << " ";
|
||||
oss << "}";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitDecoratorAnnotation(const ASTNode* node) override {
|
||||
auto* dec = static_cast<const DecoratorAnnotation*>(node);
|
||||
return "// @" + dec->name;
|
||||
}
|
||||
|
||||
std::string visitDerefStrategy(const DerefStrategy* annotation) override {
|
||||
// Generate C++-style comment for deref strategy
|
||||
if (annotation->strategy == "batched") {
|
||||
|
||||
@@ -530,6 +530,148 @@ public:
|
||||
return type->typeName;
|
||||
}
|
||||
|
||||
// --- New AST node visitors (Step 304) ---
|
||||
|
||||
std::string visitClassDeclaration(const ASTNode* node) override {
|
||||
auto* cls = static_cast<const ClassDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = cls->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
oss << "class " << cls->name;
|
||||
if (!cls->superClass.empty()) oss << "(" << cls->superClass << ")";
|
||||
oss << ":\n";
|
||||
auto fields = cls->getChildren("fields");
|
||||
auto methods = cls->getChildren("methods");
|
||||
if (fields.empty() && methods.empty()) {
|
||||
oss << " pass\n";
|
||||
} else {
|
||||
for (const auto* f : fields)
|
||||
oss << " " << generate(f) << "\n";
|
||||
for (const auto* m : methods)
|
||||
oss << generate(m);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitInterfaceDeclaration(const ASTNode* node) override {
|
||||
auto* iface = static_cast<const InterfaceDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "class " << iface->name << "(ABC):\n";
|
||||
auto methods = iface->getChildren("methods");
|
||||
if (methods.empty()) {
|
||||
oss << " pass\n";
|
||||
} else {
|
||||
for (const auto* m : methods) oss << generate(m);
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitMethodDeclaration(const ASTNode* node) override {
|
||||
auto* meth = static_cast<const MethodDeclaration*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = meth->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << " " << generate(a) << "\n";
|
||||
if (meth->isStatic) oss << " @staticmethod\n";
|
||||
oss << " def " << meth->name << "(";
|
||||
if (!meth->isStatic) oss << "self";
|
||||
auto params = meth->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (!meth->isStatic || i > 0) oss << ", ";
|
||||
oss << visitParameter(static_cast<const Parameter*>(params[i]));
|
||||
}
|
||||
oss << "):\n";
|
||||
auto body = meth->getChildren("body");
|
||||
if (body.empty()) {
|
||||
oss << " pass\n";
|
||||
} else {
|
||||
for (const auto* s : body) oss << " " << generate(s) << "\n";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitGenericType(const ASTNode* node) override {
|
||||
auto* gen = static_cast<const GenericType*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << gen->baseName << "[";
|
||||
auto params = gen->getChildren("typeParameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(params[i]);
|
||||
}
|
||||
oss << "]";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitTypeParameter(const ASTNode* node) override {
|
||||
auto* tp = static_cast<const TypeParameter*>(node);
|
||||
return tp->name;
|
||||
}
|
||||
|
||||
std::string visitAsyncFunction(const ASTNode* node) override {
|
||||
auto* af = static_cast<const AsyncFunction*>(node);
|
||||
std::ostringstream oss;
|
||||
auto annotations = af->getChildren("annotations");
|
||||
for (const auto* a : annotations) oss << generate(a) << "\n";
|
||||
oss << "async def " << af->name << "(";
|
||||
auto params = af->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";
|
||||
auto body = af->getChildren("body");
|
||||
if (body.empty()) {
|
||||
oss << " pass\n";
|
||||
} else {
|
||||
for (const auto* s : body) {
|
||||
std::string code = generate(s);
|
||||
oss << " " << code << "\n";
|
||||
}
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitAwaitExpression(const ASTNode* node) override {
|
||||
auto* aw = static_cast<const AwaitExpression*>(node);
|
||||
auto* expr = aw->getChild("expression");
|
||||
return "await " + (expr ? generate(expr) : "None");
|
||||
}
|
||||
|
||||
std::string visitLambdaExpression(const ASTNode* node) override {
|
||||
auto* lam = static_cast<const LambdaExpression*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "lambda ";
|
||||
auto params = lam->getChildren("parameters");
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << visitParameter(static_cast<const Parameter*>(params[i]));
|
||||
}
|
||||
oss << ": ";
|
||||
auto body = lam->getChildren("body");
|
||||
if (!body.empty()) {
|
||||
oss << generate(body[0]);
|
||||
} else {
|
||||
oss << "None";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitDecoratorAnnotation(const ASTNode* node) override {
|
||||
auto* dec = static_cast<const DecoratorAnnotation*>(node);
|
||||
std::ostringstream oss;
|
||||
oss << "@" << dec->name;
|
||||
auto args = dec->getChildren("arguments");
|
||||
if (!args.empty()) {
|
||||
oss << "(";
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (i > 0) oss << ", ";
|
||||
oss << generate(args[i]);
|
||||
}
|
||||
oss << ")";
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string visitDerefStrategy(const DerefStrategy* annotation) override {
|
||||
return "# @deref(" + annotation->strategy + ")";
|
||||
}
|
||||
|
||||
269
editor/tests/step304_test.cpp
Normal file
269
editor/tests/step304_test.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
// Step 304: Serialization + Dispatch for New Nodes (12 tests)
|
||||
// Tests that all 9 new AST node types (ClassDeclaration, InterfaceDeclaration,
|
||||
// MethodDeclaration, GenericType, TypeParameter, AsyncFunction, AwaitExpression,
|
||||
// LambdaExpression, DecoratorAnnotation) serialize/deserialize correctly and
|
||||
// dispatch through PythonGenerator and CppGenerator with non-empty output.
|
||||
|
||||
#include "ast/ClassDeclaration.h"
|
||||
#include "ast/GenericType.h"
|
||||
#include "ast/AsyncNodes.h"
|
||||
#include "ast/Serialization.h"
|
||||
#include "ast/PythonGenerator.h"
|
||||
#include "ast/CppGenerator.h"
|
||||
#include "ast/Module.h"
|
||||
#include "ast/Function.h"
|
||||
#include "ast/Variable.h"
|
||||
#include "ast/Statement.h"
|
||||
#include "ast/Expression.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
static int passed = 0, failed = 0;
|
||||
#define TEST(name) { std::cout << " " << #name << "... "; }
|
||||
#define PASS() { std::cout << "PASS\n"; ++passed; }
|
||||
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
|
||||
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
|
||||
|
||||
// 1. ClassDeclaration dispatch — Python generates "class Name:"
|
||||
void test_class_python_dispatch() {
|
||||
TEST(class_python_dispatch);
|
||||
auto cls = std::make_unique<ClassDeclaration>("cls1", "Animal");
|
||||
cls->superClass = "LivingThing";
|
||||
|
||||
PythonGenerator pyGen;
|
||||
std::string out = pyGen.generate(cls.get());
|
||||
CHECK(!out.empty(), "output should not be empty");
|
||||
CHECK(out.find("class Animal") != std::string::npos, "should contain 'class Animal'");
|
||||
CHECK(out.find("LivingThing") != std::string::npos, "should contain superclass");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 2. ClassDeclaration dispatch — C++ generates "class Name : public Super {"
|
||||
void test_class_cpp_dispatch() {
|
||||
TEST(class_cpp_dispatch);
|
||||
auto cls = std::make_unique<ClassDeclaration>("cls1", "Animal");
|
||||
cls->superClass = "LivingThing";
|
||||
|
||||
CppGenerator cppGen;
|
||||
std::string out = cppGen.generate(cls.get());
|
||||
CHECK(!out.empty(), "output should not be empty");
|
||||
CHECK(out.find("class Animal") != std::string::npos, "should contain 'class Animal'");
|
||||
CHECK(out.find("public LivingThing") != std::string::npos, "should contain ': public LivingThing'");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 3. InterfaceDeclaration dispatch — both generators produce output
|
||||
void test_interface_dispatch() {
|
||||
TEST(interface_dispatch);
|
||||
auto iface = std::make_unique<InterfaceDeclaration>("if1", "Drawable");
|
||||
|
||||
PythonGenerator pyGen;
|
||||
CppGenerator cppGen;
|
||||
std::string pyOut = pyGen.generate(iface.get());
|
||||
std::string cppOut = cppGen.generate(iface.get());
|
||||
CHECK(!pyOut.empty(), "Python output should not be empty");
|
||||
CHECK(!cppOut.empty(), "C++ output should not be empty");
|
||||
CHECK(pyOut.find("Drawable") != std::string::npos, "Python should contain name");
|
||||
CHECK(cppOut.find("Drawable") != std::string::npos, "C++ should contain name");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 4. MethodDeclaration dispatch — Python generates "def name(self):"
|
||||
void test_method_python_dispatch() {
|
||||
TEST(method_python_dispatch);
|
||||
auto meth = std::make_unique<MethodDeclaration>("m1", "draw");
|
||||
meth->isStatic = false;
|
||||
meth->isVirtual = true;
|
||||
|
||||
PythonGenerator pyGen;
|
||||
std::string out = pyGen.generate(meth.get());
|
||||
CHECK(!out.empty(), "output should not be empty");
|
||||
CHECK(out.find("def draw") != std::string::npos, "should contain 'def draw'");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 5. GenericType dispatch — Python "Name[T]", C++ "Name<T>"
|
||||
void test_generic_type_dispatch() {
|
||||
TEST(generic_type_dispatch);
|
||||
auto gen = std::make_unique<GenericType>("gt1", "Container");
|
||||
auto tp = new TypeParameter("tp1", "T");
|
||||
gen->addChild("typeParameters", tp);
|
||||
|
||||
PythonGenerator pyGen;
|
||||
CppGenerator cppGen;
|
||||
std::string pyOut = pyGen.generate(gen.get());
|
||||
std::string cppOut = cppGen.generate(gen.get());
|
||||
CHECK(pyOut.find("Container") != std::string::npos, "Python should contain 'Container'");
|
||||
CHECK(pyOut.find("T") != std::string::npos, "Python should contain 'T'");
|
||||
CHECK(cppOut.find("Container") != std::string::npos, "C++ should contain 'Container'");
|
||||
CHECK(cppOut.find("<") != std::string::npos, "C++ should use angle brackets");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 6. AsyncFunction dispatch — Python "async def name():", C++ "std::future<void>"
|
||||
void test_async_function_dispatch() {
|
||||
TEST(async_function_dispatch);
|
||||
auto af = std::make_unique<AsyncFunction>("af1", "fetchData");
|
||||
|
||||
PythonGenerator pyGen;
|
||||
CppGenerator cppGen;
|
||||
std::string pyOut = pyGen.generate(af.get());
|
||||
std::string cppOut = cppGen.generate(af.get());
|
||||
CHECK(pyOut.find("async def fetchData") != std::string::npos, "Python should have 'async def fetchData'");
|
||||
CHECK(cppOut.find("std::future") != std::string::npos, "C++ should have 'std::future'");
|
||||
CHECK(cppOut.find("fetchData") != std::string::npos, "C++ should have function name");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 7. AwaitExpression dispatch — Python "await expr", C++ "co_await expr"
|
||||
void test_await_expression_dispatch() {
|
||||
TEST(await_expression_dispatch);
|
||||
auto aw = std::make_unique<AwaitExpression>("aw1");
|
||||
auto call = []{ auto* c = new FunctionCall(); c->id = "fc1"; c->functionName = "getData"; return c; }();
|
||||
aw->setChild("expression", call);
|
||||
|
||||
PythonGenerator pyGen;
|
||||
CppGenerator cppGen;
|
||||
std::string pyOut = pyGen.generate(aw.get());
|
||||
std::string cppOut = cppGen.generate(aw.get());
|
||||
CHECK(pyOut.find("await") != std::string::npos, "Python should contain 'await'");
|
||||
CHECK(cppOut.find("co_await") != std::string::npos, "C++ should contain 'co_await'");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 8. LambdaExpression dispatch — Python "lambda", C++ "[captures](...)"
|
||||
void test_lambda_dispatch() {
|
||||
TEST(lambda_dispatch);
|
||||
auto lam = std::make_unique<LambdaExpression>("lam1");
|
||||
lam->captureList = {"x", "y"};
|
||||
auto param = new Parameter();
|
||||
param->id = "p1";
|
||||
param->name = "item";
|
||||
lam->addChild("parameters", param);
|
||||
|
||||
PythonGenerator pyGen;
|
||||
CppGenerator cppGen;
|
||||
std::string pyOut = pyGen.generate(lam.get());
|
||||
std::string cppOut = cppGen.generate(lam.get());
|
||||
CHECK(pyOut.find("lambda") != std::string::npos, "Python should contain 'lambda'");
|
||||
CHECK(cppOut.find("[x, y]") != std::string::npos, "C++ should contain captures '[x, y]'");
|
||||
CHECK(cppOut.find("item") != std::string::npos, "C++ should contain parameter name");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 9. DecoratorAnnotation dispatch — Python "@name", C++ "// @name"
|
||||
void test_decorator_dispatch() {
|
||||
TEST(decorator_dispatch);
|
||||
auto dec = std::make_unique<DecoratorAnnotation>("dec1", "cache");
|
||||
|
||||
PythonGenerator pyGen;
|
||||
CppGenerator cppGen;
|
||||
std::string pyOut = pyGen.generate(dec.get());
|
||||
std::string cppOut = cppGen.generate(dec.get());
|
||||
CHECK(pyOut.find("@cache") != std::string::npos, "Python should contain '@cache'");
|
||||
CHECK(cppOut.find("@cache") != std::string::npos, "C++ should contain '// @cache'");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 10. ClassDeclaration with methods — full nested dispatch
|
||||
void test_class_with_methods_dispatch() {
|
||||
TEST(class_with_methods_dispatch);
|
||||
auto cls = std::make_unique<ClassDeclaration>("cls1", "Shape");
|
||||
auto meth = new MethodDeclaration("m1", "area");
|
||||
meth->isVirtual = true;
|
||||
cls->addChild("methods", meth);
|
||||
|
||||
PythonGenerator pyGen;
|
||||
std::string pyOut = pyGen.generate(cls.get());
|
||||
CHECK(pyOut.find("class Shape") != std::string::npos, "should contain class name");
|
||||
CHECK(pyOut.find("def area") != std::string::npos, "should contain method name");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 11. AsyncFunction with body containing AwaitExpression — nested dispatch
|
||||
void test_async_with_await_dispatch() {
|
||||
TEST(async_with_await_dispatch);
|
||||
auto af = std::make_unique<AsyncFunction>("af1", "loadUser");
|
||||
auto aw = new AwaitExpression("aw1");
|
||||
aw->setChild("expression", []{ auto* c = new FunctionCall(); c->id = "fc1"; c->functionName = "httpGet"; return c; }());
|
||||
auto exprStmt = new ExpressionStatement();
|
||||
exprStmt->id = "es1";
|
||||
exprStmt->setChild("expression", aw);
|
||||
af->addChild("body", exprStmt);
|
||||
|
||||
CppGenerator cppGen;
|
||||
std::string out = cppGen.generate(af.get());
|
||||
CHECK(out.find("std::future") != std::string::npos, "should have std::future");
|
||||
CHECK(out.find("loadUser") != std::string::npos, "should have function name");
|
||||
PASS();
|
||||
}
|
||||
|
||||
// 12. All 9 new node types roundtrip through JSON and re-dispatch identically
|
||||
void test_all_new_nodes_json_roundtrip_dispatch() {
|
||||
TEST(all_new_nodes_json_roundtrip_dispatch);
|
||||
PythonGenerator pyGen;
|
||||
|
||||
// Build one of each type and verify roundtrip preserves dispatch output
|
||||
auto cls = std::make_unique<ClassDeclaration>("c1", "Foo");
|
||||
auto iface = std::make_unique<InterfaceDeclaration>("i1", "Bar");
|
||||
auto meth = std::make_unique<MethodDeclaration>("m1", "baz");
|
||||
auto gen = std::make_unique<GenericType>("g1", "Box");
|
||||
gen->addChild("typeParameters", new TypeParameter("tp1", "T"));
|
||||
auto tp = std::make_unique<TypeParameter>("tp2", "U");
|
||||
auto af = std::make_unique<AsyncFunction>("af1", "load");
|
||||
auto aw = std::make_unique<AwaitExpression>("aw1");
|
||||
aw->setChild("expression", []{ auto* c = new FunctionCall(); c->id = "fc1"; c->functionName = "f"; return c; }());
|
||||
auto lam = std::make_unique<LambdaExpression>("l1");
|
||||
lam->captureList = {"z"};
|
||||
auto dec = std::make_unique<DecoratorAnnotation>("d1", "test");
|
||||
|
||||
struct Case { const char* label; ASTNode* node; };
|
||||
Case cases[] = {
|
||||
{"ClassDeclaration", cls.get()},
|
||||
{"InterfaceDeclaration", iface.get()},
|
||||
{"MethodDeclaration", meth.get()},
|
||||
{"GenericType", gen.get()},
|
||||
{"TypeParameter", tp.get()},
|
||||
{"AsyncFunction", af.get()},
|
||||
{"AwaitExpression", aw.get()},
|
||||
{"LambdaExpression", lam.get()},
|
||||
{"DecoratorAnnotation", dec.get()},
|
||||
};
|
||||
|
||||
for (auto& c : cases) {
|
||||
std::string origOut = pyGen.generate(c.node);
|
||||
CHECK(!origOut.empty(), std::string("empty output for ") + c.label);
|
||||
|
||||
json j = toJson(c.node);
|
||||
ASTNode* restored = fromJson(j);
|
||||
CHECK(restored != nullptr, std::string("null fromJson for ") + c.label);
|
||||
CHECK(restored->conceptType == c.label, std::string("wrong type for ") + c.label);
|
||||
|
||||
std::string restoredOut = pyGen.generate(restored);
|
||||
CHECK(origOut == restoredOut, std::string("roundtrip output mismatch for ") + c.label);
|
||||
delete restored;
|
||||
}
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::cout << "=== Step 304: Serialization + Dispatch for New Nodes ===\n";
|
||||
test_class_python_dispatch();
|
||||
test_class_cpp_dispatch();
|
||||
test_interface_dispatch();
|
||||
test_method_python_dispatch();
|
||||
test_generic_type_dispatch();
|
||||
test_async_function_dispatch();
|
||||
test_await_expression_dispatch();
|
||||
test_lambda_dispatch();
|
||||
test_decorator_dispatch();
|
||||
test_class_with_methods_dispatch();
|
||||
test_async_with_await_dispatch();
|
||||
test_all_new_nodes_json_roundtrip_dispatch();
|
||||
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
|
||||
return failed > 0 ? 1 : 0;
|
||||
}
|
||||
22
progress.md
22
progress.md
@@ -1062,6 +1062,28 @@ and decorator annotations. Full JSON roundtrip via Serialization.h.
|
||||
captureList array preservation, async+await nested in module roundtrip,
|
||||
lambda with captures inside function body roundtrip
|
||||
|
||||
### Step 304: Serialization + Dispatch for New Nodes (12 tests)
|
||||
**Status:** PASS (12/12 tests)
|
||||
|
||||
All 9 new AST node types (ClassDeclaration, InterfaceDeclaration, MethodDeclaration,
|
||||
GenericType, TypeParameter, AsyncFunction, AwaitExpression, LambdaExpression,
|
||||
DecoratorAnnotation) dispatch through PythonGenerator and CppGenerator with
|
||||
language-appropriate output. JSON roundtrip preserves dispatch output identity.
|
||||
|
||||
**Key files:**
|
||||
- `editor/src/ast/PythonGenerator.h` — 9 visitor implementations: class→`class Name(Super):`,
|
||||
interface→`class Name(ABC):`, method→`def name(self):`, generic→`Name[T]`,
|
||||
async→`async def name():`, await→`await expr`, lambda→`lambda x: body`,
|
||||
decorator→`@name(args)`
|
||||
- `editor/src/ast/CppGeneratorTypes.h` — 9 visitor implementations: class→`class Name : public Super {}`,
|
||||
interface→`class Name {}`, method→`virtual void name()`, generic→`Name<T>`,
|
||||
async→`std::future<void> name()`, await→`co_await expr`, lambda→`[captures](auto x) { body }`,
|
||||
decorator→`// @name`
|
||||
- `editor/src/ast/ProjectionGenerator.h` — dispatchGenerate branches for all 9 types (lines 322-339)
|
||||
- `editor/tests/step304_test.cpp` — 12 tests: per-type Python+C++ dispatch (9 types),
|
||||
nested class-with-methods dispatch, async-with-await nested dispatch,
|
||||
all-9-types JSON roundtrip→dispatch identity check
|
||||
|
||||
---
|
||||
|
||||
# Roadmap Planning — Sprints 12-25+
|
||||
|
||||
Reference in New Issue
Block a user