Complete step466 range-for and structured binding support with tests
This commit is contained in:
@@ -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)
|
||||
|
||||
150
editor/src/ast/CppRangeStructured.h
Normal file
150
editor/src/ast/CppRangeStructured.h
Normal file
@@ -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 <nlohmann/json.hpp>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::string> 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<std::string>{});
|
||||
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<std::string> split(const std::string& s, char delim) {
|
||||
std::vector<std::string> 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;
|
||||
}
|
||||
};
|
||||
163
editor/tests/step466_test.cpp
Normal file
163
editor/tests/step466_test.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
// Step 466: Range-Based For + Structured Bindings Tests (12 tests)
|
||||
|
||||
#include "ast/CppRangeStructured.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
}
|
||||
35
progress.md
35
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)
|
||||
|
||||
Reference in New Issue
Block a user