diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index f0bdf06..656b165 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3073,4 +3073,13 @@ target_link_libraries(step465_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step466_test tests/step466_test.cpp) +target_include_directories(step466_test PRIVATE src) +target_link_libraries(step466_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) diff --git a/editor/src/ast/CppRangeStructured.h b/editor/src/ast/CppRangeStructured.h new file mode 100644 index 0000000..1cde305 --- /dev/null +++ b/editor/src/ast/CppRangeStructured.h @@ -0,0 +1,150 @@ +#pragma once + +// Step 466: Range-Based For + Structured Bindings +// Focused parsing/serialization/generation helpers for remaining C++ gaps. + +#include "ASTNode.h" + +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +class RangeForStatement : public ASTNode { +public: + std::string iteratorDecl; // e.g. "const auto& x" + std::string iteratorName; // e.g. "x" + std::string containerExpr; // e.g. "items" + std::string bodyCode; // lightweight body capture + + RangeForStatement() { conceptType = "RangeForStatement"; } +}; + +class StructuredBinding : public ASTNode { +public: + std::vector names; // e.g. key, value + std::string initializerExpr; // e.g. pair + + StructuredBinding() { conceptType = "StructuredBinding"; } +}; + +class CppRangeStructured { +public: + static bool parseRangeFor(const std::string& source, RangeForStatement& out) { + std::regex rx(R"(for\s*\(\s*([^:]+)\s*:\s*([^)]+)\)\s*\{?([^}]*)\}?)"); + std::smatch m; + if (!std::regex_search(source, m, rx)) return false; + out.iteratorDecl = trim(m[1].str()); + out.iteratorName = inferIteratorName(out.iteratorDecl); + out.containerExpr = trim(m[2].str()); + out.bodyCode = trim(m[3].str()); + return true; + } + + static bool parseStructuredBinding(const std::string& source, StructuredBinding& out) { + std::regex rx(R"(auto\s*\[\s*([^\]]+)\s*\]\s*=\s*([^;]+);?)"); + std::smatch m; + if (!std::regex_search(source, m, rx)) return false; + auto names = split(trim(m[1].str()), ','); + out.names.clear(); + for (auto& n : names) { + auto t = trim(n); + if (!t.empty()) out.names.push_back(t); + } + out.initializerExpr = trim(m[2].str()); + return !out.names.empty(); + } + + static json toJson(const RangeForStatement& r) { + return { + {"concept", r.conceptType}, + {"iteratorDecl", r.iteratorDecl}, + {"iteratorName", r.iteratorName}, + {"containerExpr", r.containerExpr}, + {"bodyCode", r.bodyCode} + }; + } + + static RangeForStatement rangeForFromJson(const json& j) { + RangeForStatement r; + r.iteratorDecl = j.value("iteratorDecl", ""); + r.iteratorName = j.value("iteratorName", ""); + r.containerExpr = j.value("containerExpr", ""); + r.bodyCode = j.value("bodyCode", ""); + return r; + } + + static json toJson(const StructuredBinding& b) { + return { + {"concept", b.conceptType}, + {"names", b.names}, + {"initializerExpr", b.initializerExpr} + }; + } + + static StructuredBinding structuredBindingFromJson(const json& j) { + StructuredBinding b; + b.names = j.value("names", std::vector{}); + b.initializerExpr = j.value("initializerExpr", ""); + return b; + } + + static std::string generateCpp(const RangeForStatement& r) { + std::ostringstream out; + out << "for (" << r.iteratorDecl << " : " << r.containerExpr << ") {\n"; + if (!r.bodyCode.empty()) out << " " << r.bodyCode << "\n"; + out << "}"; + return out.str(); + } + + static std::string generateCpp(const StructuredBinding& b) { + std::ostringstream out; + out << "auto ["; + for (size_t i = 0; i < b.names.size(); ++i) { + out << b.names[i]; + if (i + 1 < b.names.size()) out << ", "; + } + out << "] = " << b.initializerExpr << ";"; + return out.str(); + } + + static std::string projectRangeForToPython(const RangeForStatement& r) { + return "for " + r.iteratorName + " in " + r.containerExpr + ":\n pass"; + } + + static std::string projectRangeForToRust(const RangeForStatement& r) { + return "for " + r.iteratorName + " in " + r.containerExpr + " {\n // ...\n}"; + } + + static std::string projectRangeForToJava(const RangeForStatement& r) { + return "for (var " + r.iteratorName + " : " + r.containerExpr + ") {\n // ...\n}"; + } + +private: + static std::string trim(const std::string& s) { + size_t start = s.find_first_not_of(" \t\r\n"); + if (start == std::string::npos) return ""; + size_t end = s.find_last_not_of(" \t\r\n"); + return s.substr(start, end - start + 1); + } + + static std::vector split(const std::string& s, char delim) { + std::vector out; + std::string cur; + std::istringstream in(s); + while (std::getline(in, cur, delim)) out.push_back(cur); + return out; + } + + static std::string inferIteratorName(const std::string& iteratorDecl) { + auto t = trim(iteratorDecl); + size_t sp = t.find_last_of(" \t"); + if (sp == std::string::npos) return t; + std::string name = trim(t.substr(sp + 1)); + while (!name.empty() && (name[0] == '*' || name[0] == '&')) name.erase(name.begin()); + return name; + } +}; diff --git a/editor/tests/step466_test.cpp b/editor/tests/step466_test.cpp new file mode 100644 index 0000000..de2a9be --- /dev/null +++ b/editor/tests/step466_test.cpp @@ -0,0 +1,163 @@ +// Step 466: Range-Based For + Structured Bindings Tests (12 tests) + +#include "ast/CppRangeStructured.h" + +#include + +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 {} + +void test_parse_range_for_const_auto_ref() { + TEST(parse_range_for_const_auto_ref); + RangeForStatement r; + bool ok = CppRangeStructured::parseRangeFor( + "for (const auto& x : items) { total += x; }", r); + CHECK(ok, "parse should succeed"); + CHECK(r.iteratorDecl == "const auto& x", "wrong iterator decl"); + CHECK(r.iteratorName == "x", "wrong iterator name"); + CHECK(r.containerExpr == "items", "wrong container expr"); + PASS(); +} + +void test_parse_range_for_simple_auto() { + TEST(parse_range_for_simple_auto); + RangeForStatement r; + bool ok = CppRangeStructured::parseRangeFor( + "for (auto value : vec) { use(value); }", r); + CHECK(ok, "parse should succeed"); + CHECK(r.iteratorName == "value", "wrong iterator name"); + PASS(); +} + +void test_parse_structured_binding_pair() { + TEST(parse_structured_binding_pair); + StructuredBinding b; + bool ok = CppRangeStructured::parseStructuredBinding("auto [key, value] = pair;", b); + CHECK(ok, "parse should succeed"); + CHECK(b.names.size() == 2, "expected two names"); + CHECK(b.names[0] == "key" && b.names[1] == "value", "wrong binding names"); + CHECK(b.initializerExpr == "pair", "wrong initializer"); + PASS(); +} + +void test_parse_structured_binding_three_names() { + TEST(parse_structured_binding_three_names); + StructuredBinding b; + bool ok = CppRangeStructured::parseStructuredBinding("auto [a, b, c] = tupleVal;", b); + CHECK(ok, "parse should succeed"); + CHECK(b.names.size() == 3, "expected three names"); + PASS(); +} + +void test_range_for_json_roundtrip() { + TEST(range_for_json_roundtrip); + RangeForStatement r; + r.iteratorDecl = "const auto& item"; + r.iteratorName = "item"; + r.containerExpr = "items"; + r.bodyCode = "sum += item;"; + auto j = CppRangeStructured::toJson(r); + auto out = CppRangeStructured::rangeForFromJson(j); + CHECK(out.iteratorDecl == r.iteratorDecl, "iteratorDecl mismatch"); + CHECK(out.containerExpr == r.containerExpr, "containerExpr mismatch"); + PASS(); +} + +void test_structured_binding_json_roundtrip() { + TEST(structured_binding_json_roundtrip); + StructuredBinding b; + b.names = {"k", "v"}; + b.initializerExpr = "entry"; + auto j = CppRangeStructured::toJson(b); + auto out = CppRangeStructured::structuredBindingFromJson(j); + CHECK(out.names.size() == 2, "name count mismatch"); + CHECK(out.initializerExpr == "entry", "initializer mismatch"); + PASS(); +} + +void test_generate_cpp_range_for() { + TEST(generate_cpp_range_for); + RangeForStatement r; + r.iteratorDecl = "const auto& x"; + r.containerExpr = "items"; + r.bodyCode = "use(x);"; + auto out = CppRangeStructured::generateCpp(r); + CHECK(out.find("for (const auto& x : items)") != std::string::npos, "missing for header"); + CHECK(out.find("use(x);") != std::string::npos, "missing body code"); + PASS(); +} + +void test_generate_cpp_structured_binding() { + TEST(generate_cpp_structured_binding); + StructuredBinding b; + b.names = {"key", "value"}; + b.initializerExpr = "pair"; + auto out = CppRangeStructured::generateCpp(b); + CHECK(out == "auto [key, value] = pair;", "wrong generated binding"); + PASS(); +} + +void test_project_range_for_to_python() { + TEST(project_range_for_to_python); + RangeForStatement r; + r.iteratorName = "x"; + r.containerExpr = "items"; + auto out = CppRangeStructured::projectRangeForToPython(r); + CHECK(out.find("for x in items:") != std::string::npos, "missing python projection"); + PASS(); +} + +void test_project_range_for_to_rust() { + TEST(project_range_for_to_rust); + RangeForStatement r; + r.iteratorName = "x"; + r.containerExpr = "items"; + auto out = CppRangeStructured::projectRangeForToRust(r); + CHECK(out.find("for x in items") != std::string::npos, "missing rust projection"); + PASS(); +} + +void test_project_range_for_to_java() { + TEST(project_range_for_to_java); + RangeForStatement r; + r.iteratorName = "x"; + r.containerExpr = "items"; + auto out = CppRangeStructured::projectRangeForToJava(r); + CHECK(out.find("for (var x : items)") != std::string::npos, "missing java projection"); + PASS(); +} + +void test_parse_invalid_inputs_fail_gracefully() { + TEST(parse_invalid_inputs_fail_gracefully); + RangeForStatement r; + StructuredBinding b; + bool ok1 = CppRangeStructured::parseRangeFor("while (x < y) {}", r); + bool ok2 = CppRangeStructured::parseStructuredBinding("auto key = pair.first;", b); + CHECK(!ok1, "range-for parser should fail on non-range-for"); + CHECK(!ok2, "structured-binding parser should fail on non-binding"); + PASS(); +} + +int main() { + std::cout << "Step 466: Range-Based For + Structured Bindings Tests\n"; + + test_parse_range_for_const_auto_ref(); // 1 + test_parse_range_for_simple_auto(); // 2 + test_parse_structured_binding_pair(); // 3 + test_parse_structured_binding_three_names();// 4 + test_range_for_json_roundtrip(); // 5 + test_structured_binding_json_roundtrip(); // 6 + test_generate_cpp_range_for(); // 7 + test_generate_cpp_structured_binding(); // 8 + test_project_range_for_to_python(); // 9 + test_project_range_for_to_rust(); // 10 + test_project_range_for_to_java(); // 11 + test_parse_invalid_inputs_fail_gracefully();// 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index b1c4d4e..ce3f2db 100644 --- a/progress.md +++ b/progress.md @@ -6252,3 +6252,38 @@ assembly node round-trip serialization. - ARM/AArch64 parser with addressing mode support - x86/ARM code generation + simple x86→ARM opcode mapping - assembly semantic annotation mapping (`@Exec`, `@Target`, `@Risk`, `@Complexity`, `@Align`) + +### Step 466: Range-Based For + Structured Bindings +**Status:** PASS (12/12 tests) + +Adds focused support for C++ range-based `for` and structured binding forms, +including parse helpers, JSON round-trip, code generation, and cross-language +projection of range loops. + +**Files added:** +- `editor/src/ast/CppRangeStructured.h` — feature-gap helpers: + - `RangeForStatement` node model + - `StructuredBinding` node model + - parse helpers: + - `parseRangeFor(...)` + - `parseStructuredBinding(...)` + - JSON round-trip helpers for both constructs + - C++ generation helpers for both constructs + - range-for projection helpers to Python, Rust, Java +- `editor/tests/step466_test.cpp` — 12 tests covering: + - parsing of const-ref and simple range-for forms + - parsing of structured bindings (2 and 3 names) + - JSON round-trip behavior + - C++ generation for both constructs + - Python/Rust/Java projection checks + - graceful parse failure on invalid inputs +- `editor/CMakeLists.txt` — `step466_test` target + +**Verification run:** +- `cmake --build editor/build-native --target step466_test` — PASS +- `./editor/build-native/step466_test` — PASS (12/12) +- `./editor/build-native/step465_test` — PASS (8/8) regression coverage + +**Architecture gate check:** +- `editor/src/ast/CppRangeStructured.h` within header-size limit (`150` <= `600`) +- `editor/tests/step466_test.cpp` within test-file size guidance (`163` lines)