diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 4f130f1..6bf0c0e 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -133,6 +133,9 @@ target_include_directories(step38_test PRIVATE src) add_executable(step39_test tests/step39_test.cpp) target_include_directories(step39_test PRIVATE src) +add_executable(step40_test tests/step40_test.cpp) +target_include_directories(step40_test PRIVATE src) + add_executable(whetstone_editor src/main.cpp) target_include_directories(whetstone_editor PRIVATE src) # find_package(SDL2 REQUIRED) # Commented out for now to avoid build issues diff --git a/editor/tests/step40_test.cpp b/editor/tests/step40_test.cpp new file mode 100644 index 0000000..c047381 --- /dev/null +++ b/editor/tests/step40_test.cpp @@ -0,0 +1,203 @@ +// Step 40 TDD Test: C++ statement generation +// +// Tests that CppGenerator produces correct C++ for: +// 1. Assignment → "target = value;" (with semicolon) +// 2. Return → "return value;" (with semicolon) +// 3. IfStatement → "if (...) { ... } else { ... }" (braces, no colons) +// 4. WhileLoop → "while (...) { ... }" +// 5. ForLoop → range-based "for (auto iter : iterable) { ... }" +// +// Will fail to compile until CppGenerator is implemented. + +#include +#include +#include +#include "ast/Generator.h" + +static bool contains(const std::string& haystack, const std::string& needle) { + return haystack.find(needle) != std::string::npos; +} + +static bool notContains(const std::string& haystack, const std::string& needle) { + return haystack.find(needle) == std::string::npos; +} + +int main() { + int passed = 0; + int failed = 0; + + // --- Test 1: Assignment with semicolon --- + { + Assignment assign; + VariableReference* target = new VariableReference("v1", "result"); + IntegerLiteral* value = new IntegerLiteral("i1", 42); + assign.setChild("target", target); + assign.setChild("value", value); + + CppGenerator gen; + std::string output = gen.generate(&assign); + + assert(contains(output, "result") && "Assignment should contain target name"); + assert(contains(output, "=") && "Assignment should contain '='"); + assert(contains(output, "42") && "Assignment should contain value"); + assert(contains(output, ";") && "C++ assignment must end with semicolon"); + + std::cout << "Test 1 PASS: Assignment generates 'target = value;'" << std::endl; + ++passed; + + delete value; + delete target; + } + + // --- Test 2: Return statement with semicolon --- + { + Return ret; + IntegerLiteral* val = new IntegerLiteral("i1", 7); + ret.setChild("value", val); + + CppGenerator gen; + std::string output = gen.generate(&ret); + + assert(contains(output, "return") && "Return should contain 'return'"); + assert(contains(output, "7") && "Return should contain the value"); + assert(contains(output, ";") && "C++ return must end with semicolon"); + + std::cout << "Test 2 PASS: Return generates 'return value;'" << std::endl; + ++passed; + + delete val; + } + + // --- Test 3: IfStatement with braces --- + { + IfStatement ifStmt; + BooleanLiteral* cond = new BooleanLiteral("b1", true); + ifStmt.setChild("condition", cond); + + // Then branch: return 1; + Return* thenRet = new Return(); + IntegerLiteral* thenVal = new IntegerLiteral("i1", 1); + thenRet->setChild("value", thenVal); + ifStmt.addChild("thenBranch", thenRet); + + // Else branch: return 0; + Return* elseRet = new Return(); + IntegerLiteral* elseVal = new IntegerLiteral("i2", 0); + elseRet->setChild("value", elseVal); + ifStmt.addChild("elseBranch", elseRet); + + CppGenerator gen; + std::string output = gen.generate(&ifStmt); + + assert(contains(output, "if") && "IfStatement should contain 'if'"); + assert(contains(output, "(") && "Condition should be in parentheses"); + assert(contains(output, ")") && "Condition should be in parentheses"); + assert(contains(output, "{") && "C++ if should use braces"); + assert(contains(output, "}") && "C++ if should use braces"); + assert(contains(output, "else") && "Should contain else branch"); + assert(contains(output, "return") && "Should contain return statements"); + + std::cout << "Test 3 PASS: IfStatement generates if/else with braces" << std::endl; + ++passed; + + delete elseVal; + delete elseRet; + delete thenVal; + delete thenRet; + delete cond; + } + + // --- Test 4: WhileLoop with braces --- + { + WhileLoop loop; + BooleanLiteral* cond = new BooleanLiteral("b1", true); + loop.setChild("condition", cond); + + // Body: a simple assignment + Assignment* assign = new Assignment(); + VariableReference* target = new VariableReference("v1", "count"); + IntegerLiteral* value = new IntegerLiteral("i1", 0); + assign->setChild("target", target); + assign->setChild("value", value); + loop.addChild("body", assign); + + CppGenerator gen; + std::string output = gen.generate(&loop); + + assert(contains(output, "while") && "WhileLoop should contain 'while'"); + assert(contains(output, "(") && "Condition should be in parentheses"); + assert(contains(output, "{") && "C++ while should use braces"); + assert(contains(output, "}") && "C++ while should use closing brace"); + + std::cout << "Test 4 PASS: WhileLoop generates while(...) { ... }" << std::endl; + ++passed; + + delete value; + delete target; + delete assign; + delete cond; + } + + // --- Test 5: ForLoop --- + { + ForLoop loop; + loop.iteratorName = "item"; + VariableReference* iterable = new VariableReference("v1", "items"); + loop.setChild("iterable", iterable); + + // Body: expression statement + ExpressionStatement* exprStmt = new ExpressionStatement(); + FunctionCall* call = new FunctionCall(); + call->functionName = "process"; + VariableReference* arg = new VariableReference("v2", "item"); + call->addChild("arguments", arg); + exprStmt->setChild("expression", call); + loop.addChild("body", exprStmt); + + CppGenerator gen; + std::string output = gen.generate(&loop); + + assert(contains(output, "for") && "ForLoop should contain 'for'"); + assert(contains(output, "(") && "For should have parentheses"); + assert(contains(output, "item") && "For should contain iterator name"); + assert(contains(output, "{") && "C++ for should use braces"); + assert(contains(output, "}") && "C++ for should use closing brace"); + + std::cout << "Test 5 PASS: ForLoop generates for loop with braces" << std::endl; + ++passed; + + delete arg; + delete call; + delete exprStmt; + delete iterable; + } + + // --- Test 6: No Python-style syntax in statements --- + { + IfStatement ifStmt; + BooleanLiteral* cond = new BooleanLiteral("b1", false); + ifStmt.setChild("condition", cond); + Return* thenRet = new Return(); + IntegerLiteral* thenVal = new IntegerLiteral("i1", 1); + thenRet->setChild("value", thenVal); + ifStmt.addChild("thenBranch", thenRet); + + CppGenerator gen; + std::string output = gen.generate(&ifStmt); + + // Python if uses colon and indentation; C++ uses braces + assert(notContains(output, "elif") && "C++ should not contain 'elif'"); + assert(notContains(output, "pass") && "C++ should not contain 'pass'"); + + std::cout << "Test 6 PASS: Statements do not contain Python syntax" << std::endl; + ++passed; + + delete thenVal; + delete thenRet; + delete cond; + } + + // --- Summary --- + std::cout << "\n=== Step 40 Results: " << passed << " passed, " << failed << " failed ===" << std::endl; + return failed > 0 ? 1 : 0; +}