diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 6a710d2..df4a6ab 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3100,4 +3100,13 @@ target_link_libraries(step468_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step469_test tests/step469_test.cpp) +target_include_directories(step469_test PRIVATE src) +target_link_libraries(step469_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/CppInitializerStl.h b/editor/src/ast/CppInitializerStl.h new file mode 100644 index 0000000..8d63f65 --- /dev/null +++ b/editor/src/ast/CppInitializerStl.h @@ -0,0 +1,135 @@ +#pragma once + +// Step 469: Initializer Lists + STL Patterns +// Focused helpers for parsing initializer-list expressions and recognizing +// common STL/iterator loop patterns with lightweight annotations. + +#include "ASTNode.h" + +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +class InitializerListExpression : public ASTNode { +public: + std::vector elements; + + InitializerListExpression() { conceptType = "InitializerListExpression"; } +}; + +struct StlContainerInfo { + std::string rawType; // std::vector + std::string containerKind; // vector/map/set/string/unknown + std::string elementType; // int, pair<...>, ... + std::vector annotations; +}; + +struct IteratorPatternInfo { + bool isIteratorLoop = false; + std::vector annotations; // includes @Loop(...) when detected +}; + +class CppInitializerStl { +public: + static bool parseInitializerList(const std::string& source, InitializerListExpression& out) { + std::regex rx(R"(\{([^}]*)\})"); + std::smatch m; + if (!std::regex_search(source, m, rx)) return false; + out.elements = splitCsv(m[1].str()); + return true; + } + + static StlContainerInfo recognizeContainer(const std::string& declaration) { + StlContainerInfo info; + std::regex rx(R"((std::[A-Za-z_]\w*(?:\s*<[^>]+>)?)\s+[A-Za-z_]\w*)"); + std::smatch m; + if (!std::regex_search(declaration, m, rx)) { + info.containerKind = "unknown"; + info.rawType = ""; + return info; + } + + info.rawType = trim(m[1].str()); + auto lowered = toLower(info.rawType); + if (lowered.find("std::vector") == 0) info.containerKind = "vector"; + else if (lowered.find("std::map") == 0) info.containerKind = "map"; + else if (lowered.find("std::set") == 0) info.containerKind = "set"; + else if (lowered.find("std::string") == 0) info.containerKind = "string"; + else info.containerKind = "unknown"; + + info.elementType = extractTemplateArg(info.rawType); + info.annotations.push_back("@Container(" + info.containerKind + ")"); + if (!info.elementType.empty()) info.annotations.push_back("@ElementType(" + info.elementType + ")"); + return info; + } + + static IteratorPatternInfo detectIteratorPattern(const std::string& source) { + IteratorPatternInfo info; + std::string s = toLower(source); + bool hasBeginEnd = s.find(".begin()") != std::string::npos && s.find(".end()") != std::string::npos; + bool hasIteratorDecl = s.find("::iterator") != std::string::npos || s.find("auto it") != std::string::npos; + bool hasIncrement = s.find("++it") != std::string::npos || s.find("it++") != std::string::npos; + if (hasBeginEnd && hasIteratorDecl && hasIncrement) { + info.isIteratorLoop = true; + info.annotations.push_back("@Loop(iterator)"); + } + return info; + } + + static json toJson(const InitializerListExpression& e) { + return { + {"concept", e.conceptType}, + {"elements", e.elements} + }; + } + + static InitializerListExpression fromJson(const json& j) { + InitializerListExpression e; + e.elements = j.value("elements", std::vector{}); + return e; + } + +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::string toLower(std::string s) { + for (char& c : s) c = static_cast(std::tolower(static_cast(c))); + return s; + } + + static std::vector splitCsv(const std::string& in) { + std::vector out; + std::string cur; + int depth = 0; + for (char c : in) { + if (c == '<') ++depth; + if (c == '>') --depth; + if (c == ',' && depth == 0) { + auto t = trim(cur); + if (!t.empty()) out.push_back(t); + cur.clear(); + } else { + cur.push_back(c); + } + } + auto t = trim(cur); + if (!t.empty()) out.push_back(t); + return out; + } + + static std::string extractTemplateArg(const std::string& type) { + size_t lt = type.find('<'); + size_t gt = type.rfind('>'); + if (lt == std::string::npos || gt == std::string::npos || gt <= lt) return ""; + return trim(type.substr(lt + 1, gt - lt - 1)); + } +}; diff --git a/editor/tests/step469_test.cpp b/editor/tests/step469_test.cpp new file mode 100644 index 0000000..8b5f72d --- /dev/null +++ b/editor/tests/step469_test.cpp @@ -0,0 +1,138 @@ +// Step 469: Initializer Lists + STL Patterns Tests (12 tests) + +#include "ast/CppInitializerStl.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_initializer_list_simple_ints() { + TEST(parse_initializer_list_simple_ints); + InitializerListExpression e; + bool ok = CppInitializerStl::parseInitializerList("{1, 2, 3}", e); + CHECK(ok, "parse should succeed"); + CHECK(e.elements.size() == 3, "expected 3 elements"); + CHECK(e.elements[0] == "1" && e.elements[2] == "3", "wrong elements"); + PASS(); +} + +void test_parse_initializer_list_strings() { + TEST(parse_initializer_list_strings); + InitializerListExpression e; + bool ok = CppInitializerStl::parseInitializerList("{\"a\", \"b\"}", e); + CHECK(ok, "parse should succeed"); + CHECK(e.elements.size() == 2, "expected 2 elements"); + PASS(); +} + +void test_initializer_list_json_roundtrip() { + TEST(initializer_list_json_roundtrip); + InitializerListExpression e; + e.elements = {"1", "2", "3"}; + auto j = CppInitializerStl::toJson(e); + auto out = CppInitializerStl::fromJson(j); + CHECK(out.elements.size() == 3, "roundtrip element count mismatch"); + CHECK(out.elements[1] == "2", "roundtrip element mismatch"); + PASS(); +} + +void test_recognize_std_vector_container() { + TEST(recognize_std_vector_container); + auto info = CppInitializerStl::recognizeContainer("std::vector v = {1,2,3};"); + CHECK(info.containerKind == "vector", "expected vector kind"); + CHECK(info.elementType == "int", "expected int element type"); + PASS(); +} + +void test_recognize_std_map_container() { + TEST(recognize_std_map_container); + auto info = CppInitializerStl::recognizeContainer("std::map m;"); + CHECK(info.containerKind == "map", "expected map kind"); + CHECK(info.elementType.find("std::string") != std::string::npos, "expected map template args"); + PASS(); +} + +void test_recognize_std_set_container() { + TEST(recognize_std_set_container); + auto info = CppInitializerStl::recognizeContainer("std::set s;"); + CHECK(info.containerKind == "set", "expected set kind"); + PASS(); +} + +void test_recognize_std_string_container() { + TEST(recognize_std_string_container); + auto info = CppInitializerStl::recognizeContainer("std::string name;"); + CHECK(info.containerKind == "string", "expected string kind"); + PASS(); +} + +void test_unknown_container_fallback() { + TEST(unknown_container_fallback); + auto info = CppInitializerStl::recognizeContainer("CustomList c;"); + CHECK(info.containerKind == "unknown", "expected unknown kind"); + PASS(); +} + +void test_container_annotations_include_type_info() { + TEST(container_annotations_include_type_info); + auto info = CppInitializerStl::recognizeContainer("std::vector v;"); + bool hasContainer = false, hasElement = false; + for (const auto& a : info.annotations) { + if (a.find("@Container(") != std::string::npos) hasContainer = true; + if (a.find("@ElementType(") != std::string::npos) hasElement = true; + } + CHECK(hasContainer, "missing container annotation"); + CHECK(hasElement, "missing element type annotation"); + PASS(); +} + +void test_detect_iterator_pattern_begin_end_loop() { + TEST(detect_iterator_pattern_begin_end_loop); + std::string src = "for (std::vector::iterator it = v.begin(); it != v.end(); ++it) { use(*it); }"; + auto info = CppInitializerStl::detectIteratorPattern(src); + CHECK(info.isIteratorLoop, "expected iterator loop detection"); + CHECK(!info.annotations.empty(), "expected loop annotation"); + PASS(); +} + +void test_detect_iterator_pattern_auto_it_loop() { + TEST(detect_iterator_pattern_auto_it_loop); + std::string src = "for (auto it = values.begin(); it != values.end(); it++) { sum += *it; }"; + auto info = CppInitializerStl::detectIteratorPattern(src); + CHECK(info.isIteratorLoop, "expected iterator loop detection"); + PASS(); +} + +void test_non_iterator_loop_not_annotated() { + TEST(non_iterator_loop_not_annotated); + std::string src = "for (int i = 0; i < n; ++i) { sum += i; }"; + auto info = CppInitializerStl::detectIteratorPattern(src); + CHECK(!info.isIteratorLoop, "should not detect iterator loop"); + CHECK(info.annotations.empty(), "should not add loop annotations"); + PASS(); +} + +int main() { + std::cout << "Step 469: Initializer Lists + STL Patterns Tests\n"; + + test_parse_initializer_list_simple_ints(); // 1 + test_parse_initializer_list_strings(); // 2 + test_initializer_list_json_roundtrip(); // 3 + test_recognize_std_vector_container(); // 4 + test_recognize_std_map_container(); // 5 + test_recognize_std_set_container(); // 6 + test_recognize_std_string_container(); // 7 + test_unknown_container_fallback(); // 8 + test_container_annotations_include_type_info(); // 9 + test_detect_iterator_pattern_begin_end_loop(); // 10 + test_detect_iterator_pattern_auto_it_loop(); // 11 + test_non_iterator_loop_not_annotated(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 92b58c9..0de5f1b 100644 --- a/progress.md +++ b/progress.md @@ -6360,3 +6360,33 @@ named methods in Java/Python. **Architecture gate check:** - `editor/src/ast/CppOperators.h` within header-size limit (`129` <= `600`) - `editor/tests/step468_test.cpp` within test-file size guidance (`152` lines) + +### Step 469: Initializer Lists + STL Patterns +**Status:** PASS (12/12 tests) + +Adds focused support for C++ initializer-list parsing plus STL container and +iterator-loop pattern recognition with lightweight annotation outputs. + +**Files added:** +- `editor/src/ast/CppInitializerStl.h` — initializer/STL helpers: + - `InitializerListExpression` model + - initializer-list parser + - STL container recognition (`vector`, `map`, `set`, `string`) + - element-type extraction from template arguments + - iterator-loop pattern detector with `@Loop(iterator)` annotation + - JSON round-trip helpers for initializer-list node +- `editor/tests/step469_test.cpp` — 12 tests covering: + - initializer-list parsing and round-trip + - STL container kind/type recognition + - container annotation output checks + - iterator-pattern detection (`begin/end`, `auto it`) and non-iterator negative case +- `editor/CMakeLists.txt` — `step469_test` target + +**Verification run:** +- `cmake --build editor/build-native --target step469_test` — PASS +- `./editor/build-native/step469_test` — PASS (12/12) +- `./editor/build-native/step468_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/ast/CppInitializerStl.h` within header-size limit (`135` <= `600`) +- `editor/tests/step469_test.cpp` within test-file size guidance (`138` lines)