From 07f40c478652c7a6ea2e022acb20ab137bfe9dd7 Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 7 Feb 2026 08:18:51 -0700 Subject: [PATCH] Step 41 TDD test: C++ expression generation Tests BinaryOperation, UnaryOperation, VariableReference, all literals (int, float, string, bool, null->nullptr), FunctionCall, and nested expression parenthesization for CppGenerator. Co-Authored-By: Claude Opus 4.6 --- editor/CMakeLists.txt | 3 + editor/tests/step41_test.cpp | 203 +++++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 editor/tests/step41_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 6bf0c0e..e23e831 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -136,6 +136,9 @@ target_include_directories(step39_test PRIVATE src) add_executable(step40_test tests/step40_test.cpp) target_include_directories(step40_test PRIVATE src) +add_executable(step41_test tests/step41_test.cpp) +target_include_directories(step41_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/step41_test.cpp b/editor/tests/step41_test.cpp new file mode 100644 index 0000000..d8fe44c --- /dev/null +++ b/editor/tests/step41_test.cpp @@ -0,0 +1,203 @@ +// Step 41 TDD Test: C++ expression generation +// +// Tests that CppGenerator produces correct C++ for: +// 1. BinaryOperation → "left op right" with parentheses for nested ops +// 2. UnaryOperation → "op operand" (e.g., "-x", "!flag") +// 3. VariableReference → variable name +// 4. Literals → C++ equivalents (42, 3.14, "hello", true/false, nullptr) +// 5. FunctionCall → "name(args)" +// 6. Nested expressions with correct parenthesization +// +// 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; +} + +int main() { + int passed = 0; + int failed = 0; + + // --- Test 1: Simple BinaryOperation --- + { + BinaryOperation binOp("b1", "+"); + IntegerLiteral* left = new IntegerLiteral("i1", 3); + IntegerLiteral* right = new IntegerLiteral("i2", 4); + binOp.setChild("left", left); + binOp.setChild("right", right); + + CppGenerator gen; + std::string output = gen.generate(&binOp); + + assert(contains(output, "3") && "Should contain left operand"); + assert(contains(output, "+") && "Should contain operator"); + assert(contains(output, "4") && "Should contain right operand"); + + std::cout << "Test 1 PASS: BinaryOperation generates 'left op right'" << std::endl; + ++passed; + + delete right; + delete left; + } + + // --- Test 2: Nested BinaryOperation gets parenthesized --- + { + // (a + b) * c + BinaryOperation* outerOp = new BinaryOperation("b1", "*"); + + BinaryOperation* innerOp = new BinaryOperation("b2", "+"); + VariableReference* a = new VariableReference("v1", "a"); + VariableReference* b = new VariableReference("v2", "b"); + innerOp->setChild("left", a); + innerOp->setChild("right", b); + + VariableReference* c = new VariableReference("v3", "c"); + + outerOp->setChild("left", innerOp); + outerOp->setChild("right", c); + + CppGenerator gen; + std::string output = gen.generate(outerOp); + + assert(contains(output, "a") && "Should contain 'a'"); + assert(contains(output, "b") && "Should contain 'b'"); + assert(contains(output, "c") && "Should contain 'c'"); + assert(contains(output, "+") && "Should contain '+'"); + assert(contains(output, "*") && "Should contain '*'"); + // The inner addition should be parenthesized to preserve precedence + assert(contains(output, "(") && "Nested expression should have parentheses"); + + std::cout << "Test 2 PASS: Nested BinaryOperation parenthesized correctly" << std::endl; + ++passed; + + delete c; + delete b; + delete a; + delete innerOp; + delete outerOp; + } + + // --- Test 3: UnaryOperation --- + { + UnaryOperation unOp; + unOp.op = "-"; + VariableReference* operand = new VariableReference("v1", "x"); + unOp.setChild("operand", operand); + + CppGenerator gen; + std::string output = gen.generate(&unOp); + + assert(contains(output, "-") && "Should contain negation operator"); + assert(contains(output, "x") && "Should contain operand"); + + std::cout << "Test 3 PASS: UnaryOperation generates 'op operand'" << std::endl; + ++passed; + + delete operand; + } + + // --- Test 4: Integer literal --- + { + IntegerLiteral lit("i1", 42); + CppGenerator gen; + std::string output = gen.generate(&lit); + assert(contains(output, "42") && "Should contain integer value"); + + std::cout << "Test 4 PASS: IntegerLiteral generates '42'" << std::endl; + ++passed; + } + + // --- Test 5: Float literal --- + { + FloatLiteral lit("f1", "3.14"); + CppGenerator gen; + std::string output = gen.generate(&lit); + assert(contains(output, "3.14") && "Should contain float value"); + + std::cout << "Test 5 PASS: FloatLiteral generates '3.14'" << std::endl; + ++passed; + } + + // --- Test 6: String literal with quotes --- + { + StringLiteral lit("s1", "hello"); + CppGenerator gen; + std::string output = gen.generate(&lit); + assert(contains(output, "\"hello\"") && "Should contain quoted string"); + + std::cout << "Test 6 PASS: StringLiteral generates '\"hello\"'" << std::endl; + ++passed; + } + + // --- Test 7: Boolean literals → C++ true/false --- + { + BooleanLiteral trueLit("b1", true); + BooleanLiteral falseLit("b2", false); + CppGenerator gen; + + std::string trueOut = gen.generate(&trueLit); + std::string falseOut = gen.generate(&falseLit); + + assert(contains(trueOut, "true") && "True should generate 'true'"); + assert(contains(falseOut, "false") && "False should generate 'false'"); + + std::cout << "Test 7 PASS: BooleanLiterals generate 'true'/'false'" << std::endl; + ++passed; + } + + // --- Test 8: Null literal → nullptr --- + { + NullLiteral lit; + CppGenerator gen; + std::string output = gen.generate(&lit); + assert(contains(output, "nullptr") && "Null should generate 'nullptr' in C++"); + + std::cout << "Test 8 PASS: NullLiteral generates 'nullptr'" << std::endl; + ++passed; + } + + // --- Test 9: FunctionCall with arguments --- + { + FunctionCall call; + call.functionName = "calculate"; + IntegerLiteral* arg1 = new IntegerLiteral("i1", 5); + VariableReference* arg2 = new VariableReference("v1", "x"); + call.addChild("arguments", arg1); + call.addChild("arguments", arg2); + + CppGenerator gen; + std::string output = gen.generate(&call); + + assert(contains(output, "calculate") && "Should contain function name"); + assert(contains(output, "(") && "Should contain opening paren"); + assert(contains(output, ")") && "Should contain closing paren"); + assert(contains(output, "5") && "Should contain first argument"); + assert(contains(output, "x") && "Should contain second argument"); + + std::cout << "Test 9 PASS: FunctionCall generates 'name(args)'" << std::endl; + ++passed; + + delete arg2; + delete arg1; + } + + // --- Test 10: VariableReference --- + { + VariableReference ref("v1", "myVariable"); + CppGenerator gen; + std::string output = gen.generate(&ref); + assert(contains(output, "myVariable") && "Should contain variable name"); + + std::cout << "Test 10 PASS: VariableReference generates variable name" << std::endl; + ++passed; + } + + // --- Summary --- + std::cout << "\n=== Step 41 Results: " << passed << " passed, " << failed << " failed ===" << std::endl; + return failed > 0 ? 1 : 0; +}