Step 68 TDD test: Transformation engine

Tests TransformEngine: constant folding (3+4->7), dead code elimination
after return, OptimizationLock produces warning but doesn't block, and
no-op when nothing to transform. Defines TransformEngine interface.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bill
2026-02-07 08:30:51 -07:00
parent 89778c4b02
commit 01a094f28e
2 changed files with 219 additions and 0 deletions

View File

@@ -193,6 +193,9 @@ target_include_directories(step66_test PRIVATE src)
add_executable(step67_test tests/step67_test.cpp)
target_include_directories(step67_test PRIVATE src)
add_executable(step68_test tests/step68_test.cpp)
target_include_directories(step68_test PRIVATE src)
add_executable(whetstone_editor src/main.cpp)
target_include_directories(whetstone_editor PRIVATE src)
# find_package(SDL2 REQUIRED) # Commented out for now to avoid build issues

View File

@@ -0,0 +1,216 @@
// Step 68 TDD Test: Transformation engine
//
// Tests AST transformation framework:
// 1. Constant folding: 3 + 4 → 7 in AST
// 2. Dead code elimination: unreachable code after return removed
// 3. Transform checks OptimizationLock before modifying (warn, don't reject)
// 4. Respect @Loop annotations
// 5. Transform framework defines pre/post conditions
//
// Will fail until the transformation engine is implemented.
#include <iostream>
#include <string>
#include <cassert>
#include <vector>
#include <memory>
#include "ast/ASTNode.h"
#include "ast/Module.h"
#include "ast/Function.h"
#include "ast/Variable.h"
#include "ast/Statement.h"
#include "ast/Expression.h"
#include "ast/Annotation.h"
// Forward declaration — TransformEngine
class TransformEngine {
public:
struct TransformResult {
bool applied;
std::string warning; // Non-empty if warning produced
int nodesModified;
};
// Set the AST root to transform
void setRoot(ASTNode* root);
// Apply constant folding pass
TransformResult constantFolding();
// Apply dead code elimination pass
TransformResult deadCodeElimination();
// Apply all built-in transforms
std::vector<TransformResult> applyAll();
};
int main() {
int passed = 0;
int failed = 0;
// --- Test 1: Constant folding 3 + 4 → 7 ---
{
Module mod("m1", "Test", "python");
Function* fn = new Function("f1", "calc");
// return 3 + 4
Return* ret = new Return();
ret->id = "r1";
BinaryOperation* binOp = new BinaryOperation("b1", "+");
IntegerLiteral* left = new IntegerLiteral("i1", 3);
IntegerLiteral* right = new IntegerLiteral("i2", 4);
binOp->setChild("left", left);
binOp->setChild("right", right);
ret->setChild("value", binOp);
fn->addChild("body", ret);
mod.addChild("functions", fn);
TransformEngine engine;
engine.setRoot(&mod);
auto result = engine.constantFolding();
assert(result.applied && "Constant folding should apply");
assert(result.nodesModified > 0 && "Should modify at least one node");
// After folding, the return value should be IntegerLiteral(7)
auto* newValue = ret->getChild("value");
assert(newValue != nullptr && "Return should still have a value");
assert(newValue->conceptType == "IntegerLiteral" &&
"Folded result should be IntegerLiteral");
auto* foldedLit = static_cast<IntegerLiteral*>(newValue);
assert(foldedLit->value == 7 && "3 + 4 should fold to 7");
std::cout << "Test 1 PASS: Constant folding 3 + 4 → 7" << std::endl;
++passed;
delete right;
delete left;
delete binOp;
delete ret;
delete fn;
}
// --- Test 2: Dead code elimination removes code after return ---
{
Module mod("m1", "Test", "python");
Function* fn = new Function("f1", "early");
// return 1; x = 2 (dead code)
Return* ret = new Return();
ret->id = "r1";
IntegerLiteral* retVal = new IntegerLiteral("i1", 1);
ret->setChild("value", retVal);
fn->addChild("body", ret);
Assignment* deadAssign = new Assignment();
deadAssign->id = "dead1";
VariableReference* target = new VariableReference("vr1", "x");
IntegerLiteral* val = new IntegerLiteral("i2", 2);
deadAssign->setChild("target", target);
deadAssign->setChild("value", val);
fn->addChild("body", deadAssign);
mod.addChild("functions", fn);
TransformEngine engine;
engine.setRoot(&mod);
auto result = engine.deadCodeElimination();
assert(result.applied && "DCE should apply");
// The assignment after return should be removed
auto body = fn->getChildren("body");
assert(body.size() == 1 && "Only the return should remain");
assert(body[0]->conceptType == "Return" && "Remaining stmt should be Return");
std::cout << "Test 2 PASS: Dead code after return eliminated" << std::endl;
++passed;
delete val;
delete target;
delete deadAssign;
delete retVal;
delete ret;
delete fn;
}
// --- Test 3: Locked node produces warning but still transforms ---
{
Module mod("m1", "Test", "python");
Function* fn = new Function("f1", "locked");
OptimizationLock* lock = new OptimizationLock();
lock->id = "lock1";
lock->lockedBy = "user";
lock->lockReason = "Don't optimize";
lock->lockLevel = "warning";
fn->addChild("annotations", lock);
// return 5 + 3
Return* ret = new Return();
ret->id = "r1";
BinaryOperation* binOp = new BinaryOperation("b1", "+");
IntegerLiteral* left = new IntegerLiteral("i1", 5);
IntegerLiteral* right = new IntegerLiteral("i2", 3);
binOp->setChild("left", left);
binOp->setChild("right", right);
ret->setChild("value", binOp);
fn->addChild("body", ret);
mod.addChild("functions", fn);
TransformEngine engine;
engine.setRoot(&mod);
auto result = engine.constantFolding();
assert(result.applied && "Should still apply (warning level, not block)");
assert(!result.warning.empty() && "Should produce a warning for locked node");
// Should still fold
auto* newValue = ret->getChild("value");
assert(newValue->conceptType == "IntegerLiteral" && "Should still fold");
auto* lit = static_cast<IntegerLiteral*>(newValue);
assert(lit->value == 8 && "5 + 3 should fold to 8");
std::cout << "Test 3 PASS: Locked node warns but still transforms" << std::endl;
++passed;
delete right;
delete left;
delete binOp;
delete ret;
delete lock;
delete fn;
}
// --- Test 4: No transformation needed → result.applied is false ---
{
Module mod("m1", "Test", "python");
Function* fn = new Function("f1", "simple");
// return x (nothing to fold)
Return* ret = new Return();
ret->id = "r1";
VariableReference* ref = new VariableReference("vr1", "x");
ret->setChild("value", ref);
fn->addChild("body", ret);
mod.addChild("functions", fn);
TransformEngine engine;
engine.setRoot(&mod);
auto result = engine.constantFolding();
assert(!result.applied && "No constants to fold");
assert(result.nodesModified == 0 && "No nodes should be modified");
std::cout << "Test 4 PASS: No-op when nothing to transform" << std::endl;
++passed;
delete ref;
delete ret;
delete fn;
}
// --- Summary ---
std::cout << "\n=== Step 68 Results: " << passed << " passed, " << failed << " failed ===" << std::endl;
return failed > 0 ? 1 : 0;
}