Sprint 3 complete: Phases 3g+3h (Steps 68-75), all 75 steps done
Phase 3g — Optimization Pipeline (17/17 tests): Step 68: TransformEngine — constant folding, dead code elimination, OptimizationLock warning Step 69: StrategyAwareOptimizer — annotation-constrained (@Reclaim allows, @Owner blocks duplication, @Deallocate blocks reorder, @Allocate blocks dynamic) Step 70: StrategyValidator — post-optimization invariants (use-after-free, leak, aliasing) Step 71: IncrementalOptimizer — transform journal, undo by ID, provenance tracking Phase 3h — Integration & Validation (26/26 tests): Step 72: Pipeline — end-to-end parse→infer→validate→optimize→generate for Python/C++ Step 73: Error handling — empty ASTs, null roots, nonexistent IDs, unannotated code Step 74: Performance benchmarks — 1000-fn AST 1ms, JSON round-trip 4ms, 50 transforms+undo 1ms Step 75: APIDocGenerator — structured docs for 23 components across 6 categories Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
179
editor/src/IncrementalOptimizer.h
Normal file
179
editor/src/IncrementalOptimizer.h
Normal file
@@ -0,0 +1,179 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include "ast/ASTNode.h"
|
||||
#include "ast/Expression.h"
|
||||
#include "ast/Statement.h"
|
||||
|
||||
class IncrementalOptimizer {
|
||||
public:
|
||||
struct TransformRecord {
|
||||
std::string transformId;
|
||||
std::string transformName;
|
||||
std::vector<std::string> affectedNodeIds;
|
||||
};
|
||||
|
||||
void setRoot(ASTNode* root) { root_ = root; }
|
||||
|
||||
std::string applyTransform(const std::string& transformName) {
|
||||
std::string tid = "t" + std::to_string(nextId_++);
|
||||
|
||||
InternalRecord record;
|
||||
record.info.transformId = tid;
|
||||
record.info.transformName = transformName;
|
||||
|
||||
if (transformName == "constant-fold") {
|
||||
applyConstantFolding(root_, tid, record);
|
||||
} else if (transformName == "dead-code-elim") {
|
||||
applyDeadCodeElim(root_, tid, record);
|
||||
}
|
||||
|
||||
history_.push_back(std::move(record));
|
||||
return tid;
|
||||
}
|
||||
|
||||
std::vector<TransformRecord> getTransformHistory() const {
|
||||
std::vector<TransformRecord> result;
|
||||
for (const auto& r : history_) {
|
||||
result.push_back(r.info);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool undoTransform(const std::string& transformId) {
|
||||
for (auto it = history_.begin(); it != history_.end(); ++it) {
|
||||
if (it->info.transformId == transformId) {
|
||||
// Apply undo actions in reverse order
|
||||
for (auto ait = it->undoActions.rbegin(); ait != it->undoActions.rend(); ++ait) {
|
||||
ait->undo();
|
||||
}
|
||||
// Remove provenance entries for this transform
|
||||
for (const auto& nodeId : it->info.affectedNodeIds) {
|
||||
provenance_.erase(nodeId);
|
||||
}
|
||||
history_.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool undoLast() {
|
||||
if (history_.empty()) return false;
|
||||
return undoTransform(history_.back().info.transformId);
|
||||
}
|
||||
|
||||
std::string getProvenance(const std::string& nodeId) const {
|
||||
auto it = provenance_.find(nodeId);
|
||||
return it != provenance_.end() ? it->second : "";
|
||||
}
|
||||
|
||||
private:
|
||||
ASTNode* root_ = nullptr;
|
||||
int nextId_ = 1;
|
||||
|
||||
struct UndoAction {
|
||||
std::function<void()> undo;
|
||||
};
|
||||
|
||||
struct InternalRecord {
|
||||
TransformRecord info;
|
||||
std::vector<UndoAction> undoActions;
|
||||
};
|
||||
|
||||
std::vector<InternalRecord> history_;
|
||||
std::map<std::string, std::string> provenance_; // nodeId → transformId
|
||||
|
||||
void applyConstantFolding(ASTNode* node, const std::string& tid, InternalRecord& record) {
|
||||
// Bottom-up: process children first
|
||||
for (const auto& role : node->childRoles()) {
|
||||
auto children = node->getChildren(role);
|
||||
for (auto* child : children) {
|
||||
applyConstantFolding(child, tid, record);
|
||||
}
|
||||
}
|
||||
|
||||
// Check each child role for foldable BinaryOperations
|
||||
for (const auto& role : node->childRoles()) {
|
||||
auto children = node->getChildren(role);
|
||||
for (size_t i = 0; i < children.size(); ++i) {
|
||||
auto* child = children[i];
|
||||
if (child->conceptType == "BinaryOperation") {
|
||||
auto* binOp = static_cast<BinaryOperation*>(child);
|
||||
auto* leftNode = binOp->getChild("left");
|
||||
auto* rightNode = binOp->getChild("right");
|
||||
|
||||
if (leftNode && rightNode &&
|
||||
leftNode->conceptType == "IntegerLiteral" &&
|
||||
rightNode->conceptType == "IntegerLiteral") {
|
||||
|
||||
auto* leftLit = static_cast<IntegerLiteral*>(leftNode);
|
||||
auto* rightLit = static_cast<IntegerLiteral*>(rightNode);
|
||||
int foldedValue = evaluateOp(binOp->op, leftLit->value, rightLit->value);
|
||||
|
||||
auto* replacement = new IntegerLiteral("folded_" + binOp->id, foldedValue);
|
||||
|
||||
// Record undo: restore old child
|
||||
ASTNode* parent = node;
|
||||
std::string savedRole = role;
|
||||
ASTNode* oldChild = child;
|
||||
record.undoActions.push_back({[parent, savedRole, oldChild]() {
|
||||
parent->setChild(savedRole, oldChild);
|
||||
}});
|
||||
|
||||
node->setChild(role, replacement);
|
||||
|
||||
// Track provenance and affected nodes
|
||||
provenance_[replacement->id] = tid;
|
||||
record.info.affectedNodeIds.push_back(replacement->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void applyDeadCodeElim(ASTNode* node, const std::string& tid, InternalRecord& record) {
|
||||
if (node->conceptType == "Function") {
|
||||
auto body = node->getChildren("body");
|
||||
bool foundReturn = false;
|
||||
std::vector<ASTNode*> toRemove;
|
||||
|
||||
for (auto* stmt : body) {
|
||||
if (foundReturn) {
|
||||
toRemove.push_back(stmt);
|
||||
}
|
||||
if (stmt->conceptType == "Return") {
|
||||
foundReturn = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto* dead : toRemove) {
|
||||
// Record undo: re-add the removed child
|
||||
ASTNode* parent = node;
|
||||
ASTNode* removedChild = dead;
|
||||
record.undoActions.push_back({[parent, removedChild]() {
|
||||
parent->addChild("body", removedChild);
|
||||
}});
|
||||
|
||||
node->removeChild(dead);
|
||||
record.info.affectedNodeIds.push_back(dead->id);
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse
|
||||
for (auto* child : node->allChildren()) {
|
||||
applyDeadCodeElim(child, tid, record);
|
||||
}
|
||||
}
|
||||
|
||||
int evaluateOp(const std::string& op, int left, int right) {
|
||||
if (op == "+") return left + right;
|
||||
if (op == "-") return left - right;
|
||||
if (op == "*") return left * right;
|
||||
if (op == "/" && right != 0) return left / right;
|
||||
if (op == "%") return left % right;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user