Complete step468 operator overloading and friend support with tests

This commit is contained in:
Bill
2026-02-16 20:35:06 -07:00
parent 5519d24e95
commit 58e96178ab
4 changed files with 325 additions and 0 deletions

View File

@@ -3091,4 +3091,13 @@ target_link_libraries(step467_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step468_test tests/step468_test.cpp)
target_include_directories(step468_test PRIVATE src)
target_link_libraries(step468_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)

View File

@@ -0,0 +1,129 @@
#pragma once
// Step 468: Operator Overloading + Friend
// Focused node + parser helpers for C++ operator overload declarations.
#include "ASTNode.h"
#include <nlohmann/json.hpp>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
using json = nlohmann::json;
class OperatorOverload : public ASTNode {
public:
std::string operatorSymbol; // <, <<, ==, +, ...
std::vector<std::string> parameters;
std::string returnType;
bool isFriend = false;
bool isConst = false;
OperatorOverload() { conceptType = "OperatorOverload"; }
};
class CppOperators {
public:
static bool parseOperatorOverload(const std::string& decl, OperatorOverload& out) {
// e.g. bool operator<(const Foo&) const;
// e.g. friend ostream& operator<<(ostream&, const Foo&);
std::regex rx(R"(^\s*(friend\s+)?(.+?)\s+operator\s*([^\s(]+)\s*\(([^)]*)\)\s*(const)?\s*;?\s*$)");
std::smatch m;
if (!std::regex_match(decl, m, rx)) return false;
out.isFriend = m[1].matched;
out.returnType = trim(m[2].str());
out.operatorSymbol = trim(m[3].str());
out.parameters = parseParamList(m[4].str());
out.isConst = m[5].matched;
return !out.operatorSymbol.empty();
}
static json toJson(const OperatorOverload& o) {
return {
{"concept", o.conceptType},
{"operatorSymbol", o.operatorSymbol},
{"parameters", o.parameters},
{"returnType", o.returnType},
{"isFriend", o.isFriend},
{"isConst", o.isConst}
};
}
static OperatorOverload fromJson(const json& j) {
OperatorOverload o;
o.operatorSymbol = j.value("operatorSymbol", "");
o.parameters = j.value("parameters", std::vector<std::string>{});
o.returnType = j.value("returnType", "");
o.isFriend = j.value("isFriend", false);
o.isConst = j.value("isConst", false);
return o;
}
static std::string generateCppDeclaration(const OperatorOverload& o) {
std::ostringstream out;
if (o.isFriend) out << "friend ";
out << o.returnType << " operator" << o.operatorSymbol << "(";
for (size_t i = 0; i < o.parameters.size(); ++i) {
out << o.parameters[i];
if (i + 1 < o.parameters.size()) out << ", ";
}
out << ")";
if (o.isConst) out << " const";
out << ";";
return out.str();
}
static std::string projectToJavaMethod(const OperatorOverload& o) {
if (o.operatorSymbol == "<") return "int compareTo(Foo other)";
if (o.operatorSymbol == "==") return "boolean equals(Object other)";
if (o.operatorSymbol == "<<") return "String toDisplayString()";
return "Object operator" + sanitize(o.operatorSymbol) + "(Object other)";
}
static std::string projectToPythonMethod(const OperatorOverload& o) {
if (o.operatorSymbol == "<") return "def __lt__(self, other):";
if (o.operatorSymbol == "==") return "def __eq__(self, other):";
if (o.operatorSymbol == "<<") return "def __str__(self):";
return "def __op_" + sanitize(o.operatorSymbol) + "(self, other):";
}
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> parseParamList(const std::string& params) {
std::vector<std::string> out;
std::string cur;
int depth = 0;
for (char c : params) {
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 sanitize(const std::string& op) {
std::string out;
for (char c : op) {
if (std::isalnum(static_cast<unsigned char>(c))) out.push_back(c);
else out.push_back('_');
}
return out.empty() ? "unknown" : out;
}
};

View File

@@ -0,0 +1,152 @@
// Step 468: Operator Overloading + Friend Tests (12 tests)
#include "ast/CppOperators.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_member_operator_less_const() {
TEST(parse_member_operator_less_const);
OperatorOverload o;
bool ok = CppOperators::parseOperatorOverload("bool operator<(const Foo&) const;", o);
CHECK(ok, "parse should succeed");
CHECK(o.operatorSymbol == "<", "wrong operator symbol");
CHECK(o.returnType == "bool", "wrong return type");
CHECK(o.isConst, "expected const qualifier");
CHECK(!o.isFriend, "member overload should not be friend");
PASS();
}
void test_parse_friend_operator_stream_insertion() {
TEST(parse_friend_operator_stream_insertion);
OperatorOverload o;
bool ok = CppOperators::parseOperatorOverload(
"friend ostream& operator<<(ostream&, const Foo&);", o);
CHECK(ok, "parse should succeed");
CHECK(o.operatorSymbol == "<<", "wrong operator symbol");
CHECK(o.isFriend, "expected friend");
CHECK(o.parameters.size() == 2, "expected two parameters");
PASS();
}
void test_parse_operator_equals() {
TEST(parse_operator_equals);
OperatorOverload o;
bool ok = CppOperators::parseOperatorOverload(
"bool operator==(const Foo& rhs) const;", o);
CHECK(ok, "parse should succeed");
CHECK(o.operatorSymbol == "==", "wrong operator symbol");
CHECK(o.isConst, "expected const");
PASS();
}
void test_parse_operator_plus_non_const() {
TEST(parse_operator_plus_non_const);
OperatorOverload o;
bool ok = CppOperators::parseOperatorOverload(
"Foo operator+(const Foo& rhs);", o);
CHECK(ok, "parse should succeed");
CHECK(o.operatorSymbol == "+", "wrong operator symbol");
CHECK(!o.isConst, "unexpected const");
PASS();
}
void test_parse_invalid_declaration_fails() {
TEST(parse_invalid_declaration_fails);
OperatorOverload o;
bool ok = CppOperators::parseOperatorOverload("int notAnOperator(int x);", o);
CHECK(!ok, "non-operator declaration should fail");
PASS();
}
void test_operator_json_roundtrip() {
TEST(operator_json_roundtrip);
OperatorOverload o;
CppOperators::parseOperatorOverload("bool operator<(const Foo&) const;", o);
auto j = CppOperators::toJson(o);
auto out = CppOperators::fromJson(j);
CHECK(out.operatorSymbol == o.operatorSymbol, "operator symbol mismatch");
CHECK(out.returnType == o.returnType, "return type mismatch");
CHECK(out.isConst == o.isConst, "const flag mismatch");
PASS();
}
void test_generate_cpp_declaration_member() {
TEST(generate_cpp_declaration_member);
OperatorOverload o;
CppOperators::parseOperatorOverload("bool operator<(const Foo&) const;", o);
auto s = CppOperators::generateCppDeclaration(o);
CHECK(s == "bool operator<(const Foo&) const;", "wrong generated declaration");
PASS();
}
void test_generate_cpp_declaration_friend() {
TEST(generate_cpp_declaration_friend);
OperatorOverload o;
CppOperators::parseOperatorOverload("friend ostream& operator<<(ostream&, const Foo&);", o);
auto s = CppOperators::generateCppDeclaration(o);
CHECK(s.find("friend ostream& operator<<") == 0, "missing friend declaration start");
PASS();
}
void test_project_less_to_java_compare_to() {
TEST(project_less_to_java_compare_to);
OperatorOverload o;
CppOperators::parseOperatorOverload("bool operator<(const Foo&) const;", o);
auto java = CppOperators::projectToJavaMethod(o);
CHECK(java.find("compareTo") != std::string::npos, "expected compareTo projection");
PASS();
}
void test_project_less_to_python_dunder_lt() {
TEST(project_less_to_python_dunder_lt);
OperatorOverload o;
CppOperators::parseOperatorOverload("bool operator<(const Foo&) const;", o);
auto py = CppOperators::projectToPythonMethod(o);
CHECK(py.find("__lt__") != std::string::npos, "expected __lt__ projection");
PASS();
}
void test_project_equals_to_python_dunder_eq() {
TEST(project_equals_to_python_dunder_eq);
OperatorOverload o;
CppOperators::parseOperatorOverload("bool operator==(const Foo&) const;", o);
auto py = CppOperators::projectToPythonMethod(o);
CHECK(py.find("__eq__") != std::string::npos, "expected __eq__ projection");
PASS();
}
void test_project_stream_operator_to_java_display_method() {
TEST(project_stream_operator_to_java_display_method);
OperatorOverload o;
CppOperators::parseOperatorOverload("friend ostream& operator<<(ostream&, const Foo&);", o);
auto java = CppOperators::projectToJavaMethod(o);
CHECK(java.find("toDisplayString") != std::string::npos, "expected display projection");
PASS();
}
int main() {
std::cout << "Step 468: Operator Overloading + Friend Tests\n";
test_parse_member_operator_less_const(); // 1
test_parse_friend_operator_stream_insertion(); // 2
test_parse_operator_equals(); // 3
test_parse_operator_plus_non_const(); // 4
test_parse_invalid_declaration_fails(); // 5
test_operator_json_roundtrip(); // 6
test_generate_cpp_declaration_member(); // 7
test_generate_cpp_declaration_friend(); // 8
test_project_less_to_java_compare_to(); // 9
test_project_less_to_python_dunder_lt(); // 10
test_project_equals_to_python_dunder_eq(); // 11
test_project_stream_operator_to_java_display_method(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed)
<< " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -6325,3 +6325,38 @@ model projections.
**Architecture gate check:**
- `editor/src/ast/CppExceptions.h` within header-size limit (`178` <= `600`)
- `editor/tests/step467_test.cpp` within test-file size guidance (`148` lines)
### Step 468: Operator Overloading + Friend
**Status:** PASS (12/12 tests)
Adds focused support for C++ operator-overload declarations, including friend
operator forms, JSON round-trip, declaration generation, and projections to
named methods in Java/Python.
**Files added:**
- `editor/src/ast/CppOperators.h` — operator-overload helpers:
- `OperatorOverload` node model (`operatorSymbol`, params, return type,
`isFriend`, `isConst`)
- parser for member and friend operator declarations
- JSON round-trip helpers
- C++ declaration generation
- cross-language projection:
- `<` → Java `compareTo`, Python `__lt__`
- `==` → Java `equals`, Python `__eq__`
- `<<` → display/string projection methods
- `editor/tests/step468_test.cpp` — 12 tests covering:
- parsing member/friend overload forms
- const/friend flag behavior
- invalid declaration rejection
- JSON round-trip and declaration generation
- Java/Python projection behavior for key operators
- `editor/CMakeLists.txt``step468_test` target
**Verification run:**
- `cmake --build editor/build-native --target step468_test` — PASS
- `./editor/build-native/step468_test` — PASS (12/12)
- `./editor/build-native/step467_test` — PASS (12/12) regression coverage
**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)