From f7da8eee8d7b7eaa06cea447fd2fde56646502e2 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Feb 2026 11:02:25 -0700 Subject: [PATCH] Step 105: add optimization controls panel --- PROGRESS.md | 3 + editor/CMakeLists.txt | 4 + editor/src/main.cpp | 165 ++++++++++++++++++++++++++++++++++ editor/tests/step105_test.cpp | 56 ++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 editor/tests/step105_test.cpp diff --git a/PROGRESS.md b/PROGRESS.md index 8e9df6f..bccadd2 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -205,6 +205,7 @@ All 38 steps implemented and passing. Each step has a corresponding test (`step1 - [x] Step 102: **IMPLEMENTED** — Memory strategy dashboard panel with counts and JSON export (1/1 tests pass) - [x] Step 103: **IMPLEMENTED** — Side-by-side generated code view with scroll lock, target language selection, and line highlighting (1/1 tests pass) - [x] Step 104: **IMPLEMENTED** — Cross-language projection button with read-only projected tabs and annotation summary (2/2 tests pass) +- [x] Step 105: **IMPLEMENTED** — Optimization controls panel with constraint-aware buttons and summaries (2/2 tests pass) --- @@ -289,6 +290,7 @@ vcpkg's imgui 1.91.9 removed the `sdl2-binding` feature (only `sdl3-binding` exi **Step 102:** Compile and pass (1/1) **Step 103:** Compile and pass (1/1) **Step 104:** Compile and pass (2/2) +**Step 105:** Compile and pass (2/2) --- @@ -402,3 +404,4 @@ Sprint 4 in progress. Step 76 (LayoutManager) done. Next: Step 77 (custom code e | 2026-02-09 | Codex | Step 102: Memory strategy dashboard panel with counts and JSON export. 1/1 tests pass. | | 2026-02-09 | Codex | Step 103: Side-by-side generated code view with scroll lock, target language selector, and line highlighting. 1/1 tests pass. | | 2026-02-09 | Codex | Step 104: Cross-language projection button with read-only projected tabs and annotation summary. 2/2 tests pass. | +| 2026-02-09 | Codex | Step 105: Optimization controls panel with constraint-aware buttons and summaries. 2/2 tests pass. | diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index ce0ebb4..56e426a 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -566,6 +566,10 @@ add_executable(step104_test tests/step104_test.cpp) target_include_directories(step104_test PRIVATE src) target_link_libraries(step104_test PRIVATE nlohmann_json::nlohmann_json) +add_executable(step105_test tests/step105_test.cpp) +target_include_directories(step105_test PRIVATE src) +target_link_libraries(step105_test PRIVATE nlohmann_json::nlohmann_json) + find_package(SDL2 CONFIG REQUIRED) find_package(OpenGL REQUIRED) find_package(glad CONFIG REQUIRED) diff --git a/editor/src/main.cpp b/editor/src/main.cpp index 093b7f1..7f12e4f 100644 --- a/editor/src/main.cpp +++ b/editor/src/main.cpp @@ -33,6 +33,8 @@ #include "MemoryStrategyInference.h" #include "AnnotationConflict.h" #include "StrategyDashboard.h" +#include "TransformEngine.h" +#include "StrategyAwareOptimizer.h" #include "CrossLanguageProjector.h" #include "ast/Generator.h" #include "ast/Annotation.h" @@ -127,6 +129,9 @@ struct EditorState { std::vector suggestions; bool showSuggestionPopup = false; MemoryStrategyInference::Suggestion suggestionPopup; + std::string optimizeFoldSummary; + std::string optimizeDeadCodeSummary; + std::string optimizeApplySummary; BufferState* active() { return activeBuffer; } @@ -523,6 +528,20 @@ struct EditorState { (preserved ? "yes" : "no") + ").\n"; } + void refreshActiveTextFromAST() { + if (!active()) return; + active()->editBuf = active()->sync.getText(); + active()->editor.setContent(active()->editBuf, active()->language); + active()->highlightsDirty = true; + active()->modified = true; + if (lsp && active()->path.rfind("(untitled", 0) != 0) { + active()->lspVersion += 1; + lsp->didChange(toFileUri(active()->path), active()->editBuf, active()->lspVersion); + } + analysisPending = true; + analysisLastChange = ImGui::GetTime(); + } + void updateCursorPos(int bytePos) { if (!active()) return; active()->cursorLine = 1; @@ -591,6 +610,35 @@ static int countAnnotationNodes(const ASTNode* node) { return count; } +static std::string findOptimizationBlockReason(const ASTNode* node) { + if (!node) return ""; + for (auto* anno : node->getChildren("annotations")) { + if (anno->conceptType == "OwnerAnnotation") { + auto* oa = static_cast(anno); + if (oa->strategy == "Single") { + return "@Owner(Single) blocks optimization (aliasing risk)"; + } + } + if (anno->conceptType == "DeallocateAnnotation") { + auto* da = static_cast(anno); + if (da->strategy == "Explicit") { + return "@Deallocate(Explicit) blocks optimization (reorder risk)"; + } + } + if (anno->conceptType == "AllocateAnnotation") { + auto* aa = static_cast(anno); + if (aa->strategy == "Static") { + return "@Allocate(Static) blocks optimization (dynamic alloc risk)"; + } + } + } + for (auto* child : node->allChildren()) { + std::string found = findOptimizationBlockReason(child); + if (!found.empty()) return found; + } + return ""; +} + // --------------------------------------------------------------------------- // ImGui InputTextMultiline with std::string resize callback @@ -1954,6 +2002,123 @@ int main(int, char**) { ImGui::EndTabItem(); } + // Optimization controls + if (ImGui::BeginTabItem("Optimize")) { + ImGui::PushFont(uiFont); + Module* ast = state.active() ? state.active()->sync.getAST() : nullptr; + if (!state.active()) { + ImGui::TextDisabled("(no active buffer)"); + } else if (!ast) { + ImGui::TextDisabled("(no AST)"); + } else if (state.active()->readOnly) { + ImGui::TextDisabled("(read-only buffer)"); + } else { + std::string blockReason = findOptimizationBlockReason(ast); + bool blocked = !blockReason.empty(); + + auto showBlockedTooltip = [&](const std::string& reason) { + if (!reason.empty() && + ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(reason.c_str()); + ImGui::EndTooltip(); + } + }; + + if (blocked) ImGui::BeginDisabled(); + if (ImGui::Button("Constant Fold")) { + TransformEngine engine; + engine.setRoot(ast); + auto res = engine.constantFolding(); + std::string summary = "Constant Fold: "; + if (res.applied) { + summary += "applied (" + std::to_string(res.nodesModified) + " nodes)"; + } else { + summary += "no changes"; + } + if (!res.warning.empty()) { + summary += " — warning: " + res.warning; + state.outputLog += res.warning + "\n"; + } + state.optimizeFoldSummary = summary; + state.outputLog += summary + "\n"; + if (res.applied) state.refreshActiveTextFromAST(); + } + showBlockedTooltip(blockReason); + if (blocked) ImGui::EndDisabled(); + if (!state.optimizeFoldSummary.empty()) { + ImGui::TextWrapped("%s", state.optimizeFoldSummary.c_str()); + } + + ImGui::Separator(); + + if (blocked) ImGui::BeginDisabled(); + if (ImGui::Button("Dead Code Elimination")) { + TransformEngine engine; + engine.setRoot(ast); + auto res = engine.deadCodeElimination(); + std::string summary = "Dead Code Elimination: "; + if (res.applied) { + summary += "applied (" + std::to_string(res.nodesModified) + " nodes)"; + } else { + summary += "no changes"; + } + if (!res.warning.empty()) { + summary += " — warning: " + res.warning; + state.outputLog += res.warning + "\n"; + } + state.optimizeDeadCodeSummary = summary; + state.outputLog += summary + "\n"; + if (res.applied) state.refreshActiveTextFromAST(); + } + showBlockedTooltip(blockReason); + if (blocked) ImGui::EndDisabled(); + if (!state.optimizeDeadCodeSummary.empty()) { + ImGui::TextWrapped("%s", state.optimizeDeadCodeSummary.c_str()); + } + + ImGui::Separator(); + + if (blocked) ImGui::BeginDisabled(); + if (ImGui::Button("Apply All")) { + TransformEngine engine; + engine.setRoot(ast); + auto results = engine.applyAll(); + int totalNodes = 0; + bool applied = false; + std::string warnings; + for (const auto& res : results) { + totalNodes += res.nodesModified; + if (res.applied) applied = true; + if (!res.warning.empty()) { + if (!warnings.empty()) warnings += "; "; + warnings += res.warning; + } + } + std::string summary = "Apply All: "; + if (applied) { + summary += "applied (" + std::to_string(totalNodes) + " nodes)"; + } else { + summary += "no changes"; + } + if (!warnings.empty()) { + summary += " — warning: " + warnings; + state.outputLog += warnings + "\n"; + } + state.optimizeApplySummary = summary; + state.outputLog += summary + "\n"; + if (applied) state.refreshActiveTextFromAST(); + } + showBlockedTooltip(blockReason); + if (blocked) ImGui::EndDisabled(); + if (!state.optimizeApplySummary.empty()) { + ImGui::TextWrapped("%s", state.optimizeApplySummary.c_str()); + } + } + ImGui::PopFont(); + ImGui::EndTabItem(); + } + // AST view if (ImGui::BeginTabItem("AST")) { ImGui::PushFont(monoFont); diff --git a/editor/tests/step105_test.cpp b/editor/tests/step105_test.cpp new file mode 100644 index 0000000..b66e8db --- /dev/null +++ b/editor/tests/step105_test.cpp @@ -0,0 +1,56 @@ +// Step 105 TDD Test: Optimization controls +// +// Tests: +// 1. StrategyAwareOptimizer blocks optimization when @Owner(Single) present +// 2. TransformEngine constant folding modifies nodes + +#include +#include +#include "StrategyAwareOptimizer.h" +#include "TransformEngine.h" +#include "ast/Module.h" +#include "ast/Function.h" +#include "ast/Statement.h" +#include "ast/Expression.h" +#include "ast/Annotation.h" + +int main() { + int passed = 0; + int failed = 0; + + Module mod("m1", "mod", "python"); + auto* fn = new Function("f1", "foo"); + auto* owner = new OwnerAnnotation("a1", "Single"); + fn->addChild("annotations", owner); + mod.addChild("functions", fn); + + StrategyAwareOptimizer opt; + opt.setRoot(&mod); + auto res = opt.optimizeFunction("f1"); + assert(!res.blocked.empty()); + std::cout << "Test 1 PASS: optimization blocked by annotation" << std::endl; + ++passed; + + Module mod2("m2", "mod2", "python"); + auto* fn2 = new Function("f2", "bar"); + auto* ret = new Return(); + ret->id = "r1"; + auto* bin = new BinaryOperation("b1", "+"); + auto* left = new IntegerLiteral("i1", 2); + auto* right = new IntegerLiteral("i2", 3); + bin->setChild("left", left); + bin->setChild("right", right); + ret->setChild("value", bin); + fn2->addChild("body", ret); + mod2.addChild("functions", fn2); + + TransformEngine engine; + engine.setRoot(&mod2); + auto foldRes = engine.constantFolding(); + assert(foldRes.applied && foldRes.nodesModified > 0); + std::cout << "Test 2 PASS: constant folding applied" << std::endl; + ++passed; + + std::cout << "\n=== Step 105 Results: " << passed << " passed, " << failed << " failed ===" << std::endl; + return failed > 0 ? 1 : 0; +}