diff --git a/core/chip32/chip32_vm.h b/core/chip32/chip32_vm.h index 5757143..f52eb78 100644 --- a/core/chip32/chip32_vm.h +++ b/core/chip32/chip32_vm.h @@ -85,9 +85,9 @@ typedef enum OP_SKIPNZ = 27, ///< skip next instruction if not zero, e.g.: skipnz r2 // Comparison - OP_CMP_EQ = 28, ///< compare two registers for equality, result in first e.g.: eq r4, r0, r1 (r4 = (r0 == r1 ? 1 : 0) - OP_CMP_GT = 29, ///< compare if first register is greater than the second, result in first e.g.: gt r4, r0, r1 - OP_CMP_LT = 30, ///< compare if first register is less than the second, result in first e.g.: lt r4, r0, r1 + OP_CMP_EQ = 28, ///< compare two registers for equality, result in first e.g.: eq r4, r0, r1 ; similar to: r4 = (r0 == r1 ? 1 : 0 + OP_CMP_GT = 29, ///< compare if first register is greater than the second, result in first e.g.: gt r4, r0, r1 ; similar to: r4 = (r0 > r1 ? 1 : 0 + OP_CMP_LT = 30, ///< compare if first register is less than the second, result in first e.g.: lt r4, r0, r1 ; similar to: r4 = (r0 < r1 ? 1 : 0 INSTRUCTION_COUNT } chip32_instruction_t; diff --git a/core/story-manager/src/compiler/assembly_generator_chip32_tac.h b/core/story-manager/src/compiler/assembly_generator_chip32_tac.h index feacfaf..4153c05 100644 --- a/core/story-manager/src/compiler/assembly_generator_chip32_tac.h +++ b/core/story-manager/src/compiler/assembly_generator_chip32_tac.h @@ -462,13 +462,13 @@ private: void GenerateIfFalse(std::shared_ptr instr) { LoadOperand(instr->GetOp1(), "r0"); - m_assembly << " skipz r0\n"; + m_assembly << " skipnz r0\n"; m_assembly << " jump " << instr->GetDest()->ToString() << "\n"; } void GenerateIfTrue(std::shared_ptr instr) { LoadOperand(instr->GetOp1(), "r0"); - m_assembly << " skipnz r0\n"; + m_assembly << " skipz r0\n"; m_assembly << " jump " << instr->GetDest()->ToString() << "\n"; } @@ -601,9 +601,9 @@ private: break; case Variable::ValueType::INTEGER: - // CORRECTION : Déclarer 1 élément, pas la valeur ! - m_assembly << "$" << v->GetLabel() << " DV32, 1 ; " - << v->GetVariableName() << "\n"; + m_assembly << "$" << v->GetLabel() << " DV32, " + << v->GetValue() << " ; " + << v->GetVariableName() << "\n"; break; case Variable::ValueType::FLOAT: diff --git a/core/story-manager/src/compiler/tac.h b/core/story-manager/src/compiler/tac.h index 1a7d818..e4b3ae0 100644 --- a/core/story-manager/src/compiler/tac.h +++ b/core/story-manager/src/compiler/tac.h @@ -696,7 +696,13 @@ private: } void GenerateBranchNode(std::shared_ptr node) { + auto* branchNode = node->GetAs(); + if (!branchNode) { + throw std::runtime_error("BranchNode cast failed"); + } + // Évaluer la condition (le BranchNode reçoit la condition sur le port 1) + // La condition provient généralement d'un OperatorNode (comparaison) auto conditionInput = node->GetDataInput(1); if (!conditionInput) { throw std::runtime_error("BranchNode missing condition input on port 1"); @@ -707,11 +713,12 @@ private: throw std::runtime_error("BranchNode condition evaluation returned null"); } - // Créer les labels pour true/false - auto trueLabel = NewLabel("true_branch"); - auto falseLabel = NewLabel("false_branch"); - auto endLabel = NewLabel("end_branch"); + // Créer les labels pour true/false/end + auto trueLabel = NewLabel("branch_true"); + auto falseLabel = NewLabel("branch_false"); + auto endLabel = NewLabel("branch_end"); + // Convention: 0 = false, toute autre valeur = true // if (!condition) goto falseLabel auto ifInstr = std::make_shared( TACInstruction::OpCode::IF_FALSE, @@ -721,11 +728,11 @@ private: m_program.AddInstruction(ifInstr); // True branch label - auto labelInstr1 = std::make_shared( + auto labelTrue = std::make_shared( TACInstruction::OpCode::LABEL, trueLabel ); - m_program.AddInstruction(labelInstr1); + m_program.AddInstruction(labelTrue); // True branch code if (node->GetChild(0)) { @@ -740,11 +747,11 @@ private: m_program.AddInstruction(gotoEnd); // False branch label - auto labelInstr2 = std::make_shared( + auto labelFalse = std::make_shared( TACInstruction::OpCode::LABEL, falseLabel ); - m_program.AddInstruction(labelInstr2); + m_program.AddInstruction(labelFalse); // False branch code if (node->GetChild(1)) { @@ -752,11 +759,11 @@ private: } // End label - auto labelInstr3 = std::make_shared( + auto labelEnd = std::make_shared( TACInstruction::OpCode::LABEL, endLabel ); - m_program.AddInstruction(labelInstr3); + m_program.AddInstruction(labelEnd); } }; diff --git a/core/story-manager/src/nodes/branch_node.cpp b/core/story-manager/src/nodes/branch_node.cpp index 4a3e42a..b77531b 100644 --- a/core/story-manager/src/nodes/branch_node.cpp +++ b/core/story-manager/src/nodes/branch_node.cpp @@ -3,16 +3,38 @@ #include "connection.h" #include "sys_lib.h" - BranchNode::BranchNode(const std::string &type) : BaseNode(type, "Branch Node") { - + // Configuration du nœud de branchement + SetBehavior(BaseNode::BEHAVIOR_EXECUTION); + + // Port d'entrée 0: Exécution (EXECUTION_PORT) + // Port d'entrée 1: Condition (DATA_PORT) + AddInputPort(Port::EXECUTION_PORT, ">", true); + AddInputPort(Port::DATA_PORT, "condition"); + + // Port de sortie 0: Branche TRUE (EXECUTION_PORT) + // Port de sortie 1: Branche FALSE (EXECUTION_PORT) + AddOutputPort(Port::EXECUTION_PORT, "true", true); + AddOutputPort(Port::EXECUTION_PORT, "false", true); + + // Initialiser les données internes + nlohmann::json j; + j["node_type"] = "branch"; + j["version"] = 1; + SetInternalData(j); } - void BranchNode::Initialize() { - -} - + // Charger les données sauvegardées + nlohmann::json j = GetInternalData(); + + // Vérifier la version pour compatibilité future + if (j.contains("version")) + { + int version = j["version"].get(); + // Gérer les migrations de version si nécessaire + } +} \ No newline at end of file diff --git a/core/story-manager/src/nodes/branch_node.h b/core/story-manager/src/nodes/branch_node.h index f43b46c..70fa815 100644 --- a/core/story-manager/src/nodes/branch_node.h +++ b/core/story-manager/src/nodes/branch_node.h @@ -6,13 +6,32 @@ #include "i_script_node.h" #include "i_story_project.h" +/** + * @brief Nœud de branchement conditionnel simple + * + * Ce nœud évalue une condition booléenne et exécute l'une des deux branches. + * La condition provient d'un OperatorNode ou de toute autre source de données. + * + * Convention booléenne: + * - 0 = false + * - toute autre valeur = true + * + * Ports: + * - Entrée 0: Flux d'exécution (EXECUTION_PORT) + * - Entrée 1: Condition (DATA_PORT) - résultat d'un OperatorNode par exemple + * - Sortie 0: Branche TRUE (EXECUTION_PORT) + * - Sortie 1: Branche FALSE (EXECUTION_PORT) + * + * Utilisation typique: + * [Variable A] ──┐ + * ├──> [OperatorNode >] ──> [BranchNode] + * [Variable B] ──┘ ↓ ↓ + * TRUE FALSE + */ class BranchNode : public BaseNode { public: BranchNode(const std::string &type); virtual void Initialize() override; - -private: -}; - +}; \ No newline at end of file diff --git a/core/story-manager/src/nodes_factory.h b/core/story-manager/src/nodes_factory.h index d59e92f..04439ec 100644 --- a/core/story-manager/src/nodes_factory.h +++ b/core/story-manager/src/nodes_factory.h @@ -16,7 +16,7 @@ #include "story_project.h" #include "story_primitive.h" #include "function_entry_node.h" - +#include "branch_node.h" static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec"; static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb"; @@ -24,7 +24,7 @@ static const std::string VariableNodeUuid = "020cca4e-9cdc-47e7-a6a5-53e4c9152ed static const std::string PrintNodeUuid = "02ee27bc-ff1d-4f94-b700-eab55052ad1c"; static const std::string SyscallNodeUuid = "02225cff-4975-400e-8130-41524d8af773"; static const std::string FunctionEntryNodeUuid = "02fd145a-b3a6-43c2-83ce-6a187e6d4b5b"; -static const std::string DUMMY_a_prendre_Uuid = "027b723d-2327-4646-a17a-79ddc2e016e4"; +static const std::string BranchNodeUuid = "027b723d-2327-4646-a17a-79ddc2e016e4"; typedef std::shared_ptr (*GenericCreator)(const std::string &type); @@ -44,6 +44,8 @@ public: registerNode(PrintNodeUuid, std::make_shared("Print")); registerNode(SyscallNodeUuid, std::make_shared("System call")); registerNode(FunctionEntryNodeUuid, std::make_shared("Function entry")); + registerNode(BranchNodeUuid, std::make_shared("Branch")); + } ~NodesFactory() = default; diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index d3cb6bb..8e5e54a 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -30,6 +30,7 @@ add_executable(${PROJECT_NAME} test_vm.cpp test_ast.cpp test_print_node.cpp + test_branch.cpp ../story-manager/src/nodes/base_node.cpp ../story-manager/src/nodes/branch_node.cpp diff --git a/core/tests/test_branch.cpp b/core/tests/test_branch.cpp new file mode 100644 index 0000000..78fe5d5 --- /dev/null +++ b/core/tests/test_branch.cpp @@ -0,0 +1,672 @@ +// =================================================================== +// test_branch_node.cpp +// Tests complets pour BranchNode avec différents types de conditions +// =================================================================== + +#include +#include "branch_node.h" +#include "variable_node.h" +#include "function_entry_node.h" +#include "operator_node.h" +#include "print_node.h" +#include "connection.h" +#include "ast_builder.h" +#include "assembly_generator_chip32_tac.h" +#include "chip32_machine.h" +#include "variable.h" + +// =================================================================== +// TEST 1 : BranchNode avec condition booléenne directe (variable) +// =================================================================== +TEST_CASE("Branch with direct boolean variable", "[branch][boolean][variable]") { + std::cout << "\n=== Test: Branch with direct boolean variable ===\n"; + + // Variables + std::vector> variables; + + // Condition = 1 (true) + auto varCondition = std::make_shared("condition"); + varCondition->SetIntegerValue(1); + variables.push_back(varCondition); + + // Nodes + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto varNode = std::make_shared("var-condition"); + varNode->SetVariable(varCondition); + + auto branchNode = std::make_shared("branch-node"); + branchNode->Initialize(); + + auto printTrue = std::make_shared("print-true"); + printTrue->SetText("TRUE branch executed"); + printTrue->Initialize(); + + auto printFalse = std::make_shared("print-false"); + printFalse->SetText("FALSE branch executed"); + printFalse->Initialize(); + + std::vector> nodes = { + functionEntry, + varNode, + branchNode, + printTrue, + printFalse + }; + + // Connections + std::vector> connections; + + // Execution: Entry → Branch + auto execConn = std::make_shared(); + execConn->outNodeId = functionEntry->GetId(); + execConn->outPortIndex = 0; + execConn->inNodeId = branchNode->GetId(); + execConn->inPortIndex = 0; + execConn->type = Connection::EXECUTION_LINK; + connections.push_back(execConn); + + // Data: Variable → Branch.condition (port 1) + auto dataConn = std::make_shared(); + dataConn->outNodeId = varNode->GetId(); + dataConn->outPortIndex = 0; + dataConn->inNodeId = branchNode->GetId(); + dataConn->inPortIndex = 1; + dataConn->type = Connection::DATA_LINK; + connections.push_back(dataConn); + + // Execution: Branch.true → PrintTrue + auto execTrue = std::make_shared(); + execTrue->outNodeId = branchNode->GetId(); + execTrue->outPortIndex = 0; + execTrue->inNodeId = printTrue->GetId(); + execTrue->inPortIndex = 0; + execTrue->type = Connection::EXECUTION_LINK; + connections.push_back(execTrue); + + // Execution: Branch.false → PrintFalse + auto execFalse = std::make_shared(); + execFalse->outNodeId = branchNode->GetId(); + execFalse->outPortIndex = 1; + execFalse->inNodeId = printFalse->GetId(); + execFalse->inPortIndex = 0; + execFalse->type = Connection::EXECUTION_LINK; + connections.push_back(execFalse); + + // Build AST + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + // Generate Assembly using TAC + AssemblyGenerator::GeneratorContext context( + variables, + "2025-01-10 10:00:00", + "test-branch-bool-var", + true, + true, + 1024 + ); + + AssemblyGeneratorChip32TAC tacGen(context); + tacGen.GenerateCompleteProgram(nodes, pathTree); + + std::string assembly = tacGen.GetAssembly().str(); + + std::cout << "\n--- Generated Assembly ---\n"; + std::cout << assembly << std::endl; + + // Verify + REQUIRE(assembly.find("DATA") != std::string::npos); + REQUIRE(assembly.find("CODE") != std::string::npos); + REQUIRE(assembly.find("branch_true") != std::string::npos); + REQUIRE(assembly.find("branch_false") != std::string::npos); + + // Execute + std::cout << "\n--- Execution Output ---\n"; + std::cout << "Expected: TRUE branch executed (condition = 1)\n"; + std::cout << "Actual: "; + + Chip32::Machine machine; + machine.QuickExecute(assembly); + + std::cout << "\n✓ Test passed\n"; +} + +// =================================================================== +// TEST 2 : BranchNode avec OperatorNode (comparaison) +// =================================================================== +TEST_CASE("Branch with OperatorNode comparison", "[branch][operator][comparison]") { + std::cout << "\n=== Test: Branch with OperatorNode (A > B) ===\n"; + + // Variables + std::vector> variables; + + auto varA = std::make_shared("A"); + varA->SetIntegerValue(10); + variables.push_back(varA); + + auto varB = std::make_shared("B"); + varB->SetIntegerValue(5); + variables.push_back(varB); + + // Nodes + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto varNodeA = std::make_shared("var-A"); + varNodeA->SetVariable(varA); + + auto varNodeB = std::make_shared("var-B"); + varNodeB->SetVariable(varB); + + auto operatorNode = std::make_shared("operator-gt"); + operatorNode->SetOperationType(OperatorNode::OperationType::GREATER_THAN); + operatorNode->Initialize(); + + auto branchNode = std::make_shared("branch-node"); + branchNode->Initialize(); + + auto printTrue = std::make_shared("print-true"); + printTrue->SetText("A is greater than B"); + printTrue->Initialize(); + + auto printFalse = std::make_shared("print-false"); + printFalse->SetText("A is NOT greater than B"); + printFalse->Initialize(); + + std::vector> nodes = { + functionEntry, + varNodeA, + varNodeB, + operatorNode, + branchNode, + printTrue, + printFalse + }; + + // Connections + std::vector> connections; + + // Execution: Entry → Branch + auto execConn = std::make_shared(); + execConn->outNodeId = functionEntry->GetId(); + execConn->outPortIndex = 0; + execConn->inNodeId = branchNode->GetId(); + execConn->inPortIndex = 0; + execConn->type = Connection::EXECUTION_LINK; + connections.push_back(execConn); + + // Data: A → Operator.in1 + auto dataConn1 = std::make_shared(); + dataConn1->outNodeId = varNodeA->GetId(); + dataConn1->outPortIndex = 0; + dataConn1->inNodeId = operatorNode->GetId(); + dataConn1->inPortIndex = 0; + dataConn1->type = Connection::DATA_LINK; + connections.push_back(dataConn1); + + // Data: B → Operator.in2 + auto dataConn2 = std::make_shared(); + dataConn2->outNodeId = varNodeB->GetId(); + dataConn2->outPortIndex = 0; + dataConn2->inNodeId = operatorNode->GetId(); + dataConn2->inPortIndex = 1; + dataConn2->type = Connection::DATA_LINK; + connections.push_back(dataConn2); + + // Data: Operator.out → Branch.condition + auto dataConn3 = std::make_shared(); + dataConn3->outNodeId = operatorNode->GetId(); + dataConn3->outPortIndex = 0; + dataConn3->inNodeId = branchNode->GetId(); + dataConn3->inPortIndex = 1; + dataConn3->type = Connection::DATA_LINK; + connections.push_back(dataConn3); + + // Execution: Branch.true → PrintTrue + auto execTrue = std::make_shared(); + execTrue->outNodeId = branchNode->GetId(); + execTrue->outPortIndex = 0; + execTrue->inNodeId = printTrue->GetId(); + execTrue->inPortIndex = 0; + execTrue->type = Connection::EXECUTION_LINK; + connections.push_back(execTrue); + + // Execution: Branch.false → PrintFalse + auto execFalse = std::make_shared(); + execFalse->outNodeId = branchNode->GetId(); + execFalse->outPortIndex = 1; + execFalse->inNodeId = printFalse->GetId(); + execFalse->inPortIndex = 0; + execFalse->type = Connection::EXECUTION_LINK; + connections.push_back(execFalse); + + // Build and generate + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + AssemblyGenerator::GeneratorContext context( + variables, "2025-01-10", "test-branch-operator", true, true, 1024 + ); + + AssemblyGeneratorChip32TAC tacGen(context); + tacGen.GenerateCompleteProgram(nodes, pathTree); + + std::string assembly = tacGen.GetAssembly().str(); + + std::cout << "\n--- Generated Assembly ---\n"; + std::cout << assembly << std::endl; + + // Verify TAC structure + REQUIRE(assembly.find("gt") != std::string::npos); // Greater Than opcode + REQUIRE(assembly.find("branch_true") != std::string::npos); + REQUIRE(assembly.find("branch_false") != std::string::npos); + + // Execute + std::cout << "\n--- Execution Output ---\n"; + std::cout << "Expected: A is greater than B (10 > 5)\n"; + std::cout << "Actual: "; + + Chip32::Machine machine; + machine.QuickExecute(assembly); + + std::cout << "\n✓ Test passed\n"; +} + +// =================================================================== +// TEST 3 : Tester différentes valeurs (0 = false, autres = true) +// =================================================================== +TEST_CASE("Branch with various condition values", "[branch][values][convention]") { + std::cout << "\n=== Test: Branch with various condition values ===\n"; + + struct TestCase { + int conditionValue; + bool expectedTrue; + std::string description; + }; + + std::vector testCases = { + {0, false, "0 should be FALSE"}, + {1, true, "1 should be TRUE"}, + {42, true, "42 should be TRUE"}, + {-5, true, "-5 should be TRUE"}, + {100, true, "100 should be TRUE"} + }; + + for (const auto& test : testCases) { + std::cout << "\n--- Testing: " << test.description << " ---\n"; + + // Variables + std::vector> variables; + + auto varCondition = std::make_shared("condition"); + varCondition->SetIntegerValue(test.conditionValue); + variables.push_back(varCondition); + + // Nodes + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto varNode = std::make_shared("var-condition"); + varNode->SetVariable(varCondition); + + auto branchNode = std::make_shared("branch-node"); + branchNode->Initialize(); + + auto printTrue = std::make_shared("print-true"); + printTrue->SetText("TRUE"); + printTrue->Initialize(); + + auto printFalse = std::make_shared("print-false"); + printFalse->SetText("FALSE"); + printFalse->Initialize(); + + std::vector> nodes = { + functionEntry, varNode, branchNode, printTrue, printFalse + }; + + // Connections (même pattern que TEST 1) + std::vector> connections; + + auto execConn = std::make_shared(); + execConn->outNodeId = functionEntry->GetId(); + execConn->outPortIndex = 0; + execConn->inNodeId = branchNode->GetId(); + execConn->inPortIndex = 0; + execConn->type = Connection::EXECUTION_LINK; + connections.push_back(execConn); + + auto dataConn = std::make_shared(); + dataConn->outNodeId = varNode->GetId(); + dataConn->outPortIndex = 0; + dataConn->inNodeId = branchNode->GetId(); + dataConn->inPortIndex = 1; + dataConn->type = Connection::DATA_LINK; + connections.push_back(dataConn); + + auto execTrue = std::make_shared(); + execTrue->outNodeId = branchNode->GetId(); + execTrue->outPortIndex = 0; + execTrue->inNodeId = printTrue->GetId(); + execTrue->inPortIndex = 0; + execTrue->type = Connection::EXECUTION_LINK; + connections.push_back(execTrue); + + auto execFalse = std::make_shared(); + execFalse->outNodeId = branchNode->GetId(); + execFalse->outPortIndex = 1; + execFalse->inNodeId = printFalse->GetId(); + execFalse->inPortIndex = 0; + execFalse->type = Connection::EXECUTION_LINK; + connections.push_back(execFalse); + + // Build and generate + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + AssemblyGenerator::GeneratorContext context( + variables, "2025-01-10", "test-branch-values", false, true, 1024 + ); + + AssemblyGeneratorChip32TAC tacGen(context); + tacGen.GenerateCompleteProgram(nodes, pathTree); + + std::string assembly = tacGen.GetAssembly().str(); + + // Execute + Chip32::Machine machine; + machine.QuickExecute(assembly); + + std::string output = machine.printOutput; + std::string expected = test.expectedTrue ? "TRUE" : "FALSE"; + + std::cout << "Condition value: " << test.conditionValue << "\n"; + std::cout << "Expected: " << expected << ", Got: " << output << "\n"; + + REQUIRE(output.find(expected) != std::string::npos); + } + + std::cout << "\n✓ All value tests passed\n"; +} + +// =================================================================== +// TEST 4 : Tester tous les opérateurs de comparaison +// =================================================================== +TEST_CASE("Branch with all comparison operators", "[branch][operators][all]") { + std::cout << "\n=== Test: All comparison operators ===\n"; + + struct TestCase { + OperatorNode::OperationType op; + int a, b; + bool expectedTrue; + std::string description; + }; + + std::vector testCases = { + {OperatorNode::OperationType::EQUAL, 5, 5, true, "5 == 5"}, + {OperatorNode::OperationType::EQUAL, 5, 10, false, "5 == 10"}, + {OperatorNode::OperationType::NOT_EQUAL, 5, 10, true, "5 != 10"}, + {OperatorNode::OperationType::NOT_EQUAL, 5, 5, false, "5 != 5"}, + {OperatorNode::OperationType::GREATER_THAN, 10, 5, true, "10 > 5"}, + {OperatorNode::OperationType::GREATER_THAN, 5, 10, false, "5 > 10"}, + {OperatorNode::OperationType::LESS_THAN, 5, 10, true, "5 < 10"}, + {OperatorNode::OperationType::LESS_THAN, 10, 5, false, "10 < 5"}, + {OperatorNode::OperationType::GREATER_EQUAL, 10, 5, true, "10 >= 5"}, + {OperatorNode::OperationType::GREATER_EQUAL, 5, 5, true, "5 >= 5"}, + {OperatorNode::OperationType::GREATER_EQUAL, 5, 10, false, "5 >= 10"}, + {OperatorNode::OperationType::LESS_EQUAL, 5, 10, true, "5 <= 10"}, + {OperatorNode::OperationType::LESS_EQUAL, 5, 5, true, "5 <= 5"}, + {OperatorNode::OperationType::LESS_EQUAL, 10, 5, false, "10 <= 5"} + }; + + for (const auto& test : testCases) { + std::cout << "\n--- Testing: " << test.description << " ---\n"; + + // Variables + std::vector> variables; + + auto varA = std::make_shared("A"); + varA->SetIntegerValue(test.a); + variables.push_back(varA); + + auto varB = std::make_shared("B"); + varB->SetIntegerValue(test.b); + variables.push_back(varB); + + // Nodes (même structure que TEST 2) + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto varNodeA = std::make_shared("var-A"); + varNodeA->SetVariable(varA); + + auto varNodeB = std::make_shared("var-B"); + varNodeB->SetVariable(varB); + + auto operatorNode = std::make_shared("operator"); + operatorNode->SetOperationType(test.op); + operatorNode->Initialize(); + + auto branchNode = std::make_shared("branch-node"); + branchNode->Initialize(); + + auto printTrue = std::make_shared("print-true"); + printTrue->SetText("TRUE"); + printTrue->Initialize(); + + auto printFalse = std::make_shared("print-false"); + printFalse->SetText("FALSE"); + printFalse->Initialize(); + + std::vector> nodes = { + functionEntry, varNodeA, varNodeB, operatorNode, branchNode, printTrue, printFalse + }; + + // Connections (pattern complet) + std::vector> connections; + + auto execConn = std::make_shared(); + execConn->outNodeId = functionEntry->GetId(); + execConn->outPortIndex = 0; + execConn->inNodeId = branchNode->GetId(); + execConn->inPortIndex = 0; + execConn->type = Connection::EXECUTION_LINK; + connections.push_back(execConn); + + auto dataConn1 = std::make_shared(); + dataConn1->outNodeId = varNodeA->GetId(); + dataConn1->outPortIndex = 0; + dataConn1->inNodeId = operatorNode->GetId(); + dataConn1->inPortIndex = 0; + dataConn1->type = Connection::DATA_LINK; + connections.push_back(dataConn1); + + auto dataConn2 = std::make_shared(); + dataConn2->outNodeId = varNodeB->GetId(); + dataConn2->outPortIndex = 0; + dataConn2->inNodeId = operatorNode->GetId(); + dataConn2->inPortIndex = 1; + dataConn2->type = Connection::DATA_LINK; + connections.push_back(dataConn2); + + auto dataConn3 = std::make_shared(); + dataConn3->outNodeId = operatorNode->GetId(); + dataConn3->outPortIndex = 0; + dataConn3->inNodeId = branchNode->GetId(); + dataConn3->inPortIndex = 1; + dataConn3->type = Connection::DATA_LINK; + connections.push_back(dataConn3); + + auto execTrue = std::make_shared(); + execTrue->outNodeId = branchNode->GetId(); + execTrue->outPortIndex = 0; + execTrue->inNodeId = printTrue->GetId(); + execTrue->inPortIndex = 0; + execTrue->type = Connection::EXECUTION_LINK; + connections.push_back(execTrue); + + auto execFalse = std::make_shared(); + execFalse->outNodeId = branchNode->GetId(); + execFalse->outPortIndex = 1; + execFalse->inNodeId = printFalse->GetId(); + execFalse->inPortIndex = 0; + execFalse->type = Connection::EXECUTION_LINK; + connections.push_back(execFalse); + + // Build and execute + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + AssemblyGenerator::GeneratorContext context( + variables, "2025-01-10", "test-branch-op", false, true, 1024 + ); + + AssemblyGeneratorChip32TAC tacGen(context); + tacGen.GenerateCompleteProgram(nodes, pathTree); + + std::string assembly = tacGen.GetAssembly().str(); + + Chip32::Machine machine; + machine.QuickExecute(assembly); + + std::string output = machine.printOutput; + std::string expected = test.expectedTrue ? "TRUE" : "FALSE"; + + std::cout << "Expected: " << expected << ", Got: " << output << "\n"; + REQUIRE(output.find(expected) != std::string::npos); + } + + std::cout << "\n✓ All operator tests passed\n"; +} + +// =================================================================== +// TEST 5 : BranchNode avec résultat d'opération arithmétique +// =================================================================== +TEST_CASE("Branch with arithmetic operation result", "[branch][arithmetic]") { + std::cout << "\n=== Test: Branch with arithmetic result (5 - 5 = 0) ===\n"; + + // Variables + std::vector> variables; + + auto varA = std::make_shared("A"); + varA->SetIntegerValue(5); + variables.push_back(varA); + + auto varB = std::make_shared("B"); + varB->SetIntegerValue(5); + variables.push_back(varB); + + // Nodes + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto varNodeA = std::make_shared("var-A"); + varNodeA->SetVariable(varA); + + auto varNodeB = std::make_shared("var-B"); + varNodeB->SetVariable(varB); + + // Soustraction: 5 - 5 = 0 (false) + auto subNode = std::make_shared("operator-sub"); + subNode->SetOperationType(OperatorNode::OperationType::SUBTRACT); + subNode->Initialize(); + + auto branchNode = std::make_shared("branch-node"); + branchNode->Initialize(); + + auto printTrue = std::make_shared("print-true"); + printTrue->SetText("Result is NON-ZERO"); + printTrue->Initialize(); + + auto printFalse = std::make_shared("print-false"); + printFalse->SetText("Result is ZERO"); + printFalse->Initialize(); + + std::vector> nodes = { + functionEntry, varNodeA, varNodeB, subNode, branchNode, printTrue, printFalse + }; + + // Connections + std::vector> connections; + + auto execConn = std::make_shared(); + execConn->outNodeId = functionEntry->GetId(); + execConn->outPortIndex = 0; + execConn->inNodeId = branchNode->GetId(); + execConn->inPortIndex = 0; + execConn->type = Connection::EXECUTION_LINK; + connections.push_back(execConn); + + auto dataConn1 = std::make_shared(); + dataConn1->outNodeId = varNodeA->GetId(); + dataConn1->outPortIndex = 0; + dataConn1->inNodeId = subNode->GetId(); + dataConn1->inPortIndex = 0; + dataConn1->type = Connection::DATA_LINK; + connections.push_back(dataConn1); + + auto dataConn2 = std::make_shared(); + dataConn2->outNodeId = varNodeB->GetId(); + dataConn2->outPortIndex = 0; + dataConn2->inNodeId = subNode->GetId(); + dataConn2->inPortIndex = 1; + dataConn2->type = Connection::DATA_LINK; + connections.push_back(dataConn2); + + auto dataConn3 = std::make_shared(); + dataConn3->outNodeId = subNode->GetId(); + dataConn3->outPortIndex = 0; + dataConn3->inNodeId = branchNode->GetId(); + dataConn3->inPortIndex = 1; + dataConn3->type = Connection::DATA_LINK; + connections.push_back(dataConn3); + + auto execTrue = std::make_shared(); + execTrue->outNodeId = branchNode->GetId(); + execTrue->outPortIndex = 0; + execTrue->inNodeId = printTrue->GetId(); + execTrue->inPortIndex = 0; + execTrue->type = Connection::EXECUTION_LINK; + connections.push_back(execTrue); + + auto execFalse = std::make_shared(); + execFalse->outNodeId = branchNode->GetId(); + execFalse->outPortIndex = 1; + execFalse->inNodeId = printFalse->GetId(); + execFalse->inPortIndex = 0; + execFalse->type = Connection::EXECUTION_LINK; + connections.push_back(execFalse); + + // Build and execute + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + AssemblyGenerator::GeneratorContext context( + variables, "2025-01-10", "test-branch-arithmetic", true, true, 1024 + ); + + AssemblyGeneratorChip32TAC tacGen(context); + tacGen.GenerateCompleteProgram(nodes, pathTree); + + std::string assembly = tacGen.GetAssembly().str(); + + std::cout << "\n--- Generated Assembly ---\n"; + std::cout << assembly << std::endl; + + // Verify subtraction is present + REQUIRE(assembly.find("sub") != std::string::npos); + + // Execute + std::cout << "\n--- Execution Output ---\n"; + std::cout << "Expected: Result is ZERO (5 - 5 = 0)\n"; + std::cout << "Actual: "; + + Chip32::Machine machine; + machine.QuickExecute(assembly); + + std::string output = machine.printOutput; + REQUIRE(output.find("ZERO") != std::string::npos); + + std::cout << "\n✓ Test passed\n"; +} \ No newline at end of file diff --git a/story-editor/CMakeLists.txt b/story-editor/CMakeLists.txt index aa0bb24..d3b38de 100644 --- a/story-editor/CMakeLists.txt +++ b/story-editor/CMakeLists.txt @@ -148,6 +148,7 @@ set(SRCS src/node_editor/operator_node_widget.cpp src/node_editor/print_node_widget.cpp src/node_editor/syscall_node_widget.cpp + src/node_editor/branch_node_widget.cpp src/gui.cpp src/media_converter.cpp diff --git a/story-editor/imgui.ini b/story-editor/imgui.ini index 22e98a7..8f3681c 100644 --- a/story-editor/imgui.ini +++ b/story-editor/imgui.ini @@ -1,6 +1,6 @@ [Window][WindowOverViewport_11111111] Pos=60,26 -Size=1220,836 +Size=1429,832 Collapsed=0 [Window][Debug##Default] @@ -9,32 +9,32 @@ Size=400,400 Collapsed=0 [Window][Library Manager] -Pos=630,26 -Size=650,518 +Pos=554,26 +Size=935,607 Collapsed=0 DockId=0x00000002,0 [Window][Console] -Pos=60,546 -Size=628,316 +Pos=60,635 +Size=618,223 Collapsed=0 DockId=0x00000004,0 [Window][Emulator] -Pos=630,26 -Size=650,518 +Pos=554,26 +Size=935,607 Collapsed=0 DockId=0x00000002,5 [Window][Code viewer] -Pos=630,26 -Size=650,518 +Pos=554,26 +Size=935,607 Collapsed=0 DockId=0x00000002,4 [Window][Resources] -Pos=630,26 -Size=650,518 +Pos=554,26 +Size=935,607 Collapsed=0 DockId=0x00000002,1 @@ -50,36 +50,36 @@ Size=150,42 Collapsed=0 [Window][Variables] -Pos=690,546 -Size=590,316 +Pos=680,635 +Size=809,223 Collapsed=0 DockId=0x00000005,0 [Window][CPU] -Pos=630,26 -Size=650,518 +Pos=554,26 +Size=935,607 Collapsed=0 DockId=0x00000002,2 [Window][RAM view] -Pos=630,26 -Size=650,518 +Pos=554,26 +Size=935,607 Collapsed=0 DockId=0x00000002,3 [Window][Properties] -Pos=690,546 -Size=590,316 +Pos=680,635 +Size=809,223 Collapsed=0 DockId=0x00000005,1 [Window][ToolBar] Pos=0,26 -Size=60,836 +Size=60,832 Collapsed=0 [Window][QuitConfirm] -Pos=508,383 +Pos=508,312 Size=264,96 Collapsed=0 @@ -90,13 +90,13 @@ Collapsed=0 [Window][Module editor] Pos=60,26 -Size=568,518 +Size=492,607 Collapsed=0 DockId=0x00000001,0 [Window][Story editor] Pos=60,26 -Size=568,518 +Size=492,607 Collapsed=0 DockId=0x00000001,1 @@ -106,8 +106,8 @@ Size=687,422 Collapsed=0 [Window][Error List] -Pos=60,546 -Size=628,316 +Pos=60,635 +Size=618,223 Collapsed=0 DockId=0x00000004,1 @@ -155,11 +155,11 @@ Column 2 Weight=1.0000 Column 3 Width=60 [Docking][Data] -DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,836 Split=Y - DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,549 Split=X - DockNode ID=0x00000001 Parent=0x00000007 SizeRef=1144,694 CentralNode=1 Selected=0x93ADCAAB - DockNode ID=0x00000002 Parent=0x00000007 SizeRef=650,694 Selected=0xE5897A33 - DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,316 Split=X Selected=0xEA83D666 - DockNode ID=0x00000004 Parent=0x00000008 SizeRef=616,192 Selected=0xEA83D666 - DockNode ID=0x00000005 Parent=0x00000008 SizeRef=578,192 Selected=0x8C72BEA8 +DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1429,832 Split=Y + DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,469 Split=X + DockNode ID=0x00000001 Parent=0x00000007 SizeRef=492,694 CentralNode=1 Selected=0x93ADCAAB + DockNode ID=0x00000002 Parent=0x00000007 SizeRef=935,694 Selected=0x63869CAF + DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,223 Split=X Selected=0xEA83D666 + DockNode ID=0x00000004 Parent=0x00000008 SizeRef=621,192 Selected=0xEA83D666 + DockNode ID=0x00000005 Parent=0x00000008 SizeRef=813,192 Selected=0x8C72BEA8 diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index 700901b..3e46c34 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -34,7 +34,7 @@ #include "print_node_widget.h" #include "syscall_node_widget.h" #include "function_entry_widget.h" - +#include "branch_node_widget.h" MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController) : m_logger(logger) @@ -74,6 +74,7 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo m_widgetFactory.registerNode(PrintNodeUuid); m_widgetFactory.registerNode(SyscallNodeUuid); m_widgetFactory.registerNode(FunctionEntryNodeUuid); + m_widgetFactory.registerNode(BranchNodeUuid); m_eventBus.Subscribe([this](const OpenProjectEvent &event) { OpenProject(event.GetUuid()); diff --git a/story-editor/src/node_editor/branch_node_widget.cpp b/story-editor/src/node_editor/branch_node_widget.cpp new file mode 100644 index 0000000..12b9d1f --- /dev/null +++ b/story-editor/src/node_editor/branch_node_widget.cpp @@ -0,0 +1,54 @@ +#include "branch_node_widget.h" + +#include +#include "IconsMaterialDesignIcons.h" +#include "story_project.h" + +BranchNodeWidget::BranchNodeWidget(IStoryManager &manager, std::shared_ptr node) + : BaseNodeWidget(manager, node) +{ + m_branchNode = std::dynamic_pointer_cast(node); +} + +void BranchNodeWidget::Initialize() +{ + BaseNodeWidget::Initialize(); +} + +void BranchNodeWidget::DrawProperties(std::shared_ptr story) +{ + ImGui::AlignTextToFramePadding(); + + // Informations sur le nœud de branchement + ImGui::SeparatorText("Branch Node Properties"); + + ImGui::TextWrapped("This node evaluates a boolean condition and executes " + "one of two paths based on the result."); + + ImGui::Spacing(); + ImGui::Separator(); + ImGui::Spacing(); +} + +void BranchNodeWidget::Draw() +{ + // Icône et texte du nœud + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.2f, 1.0f)); + ImGui::Text(ICON_MDI_SOURCE_BRANCH " Branch"); + ImGui::PopStyleColor(); + + ImGui::Spacing(); + + // Affichage de la condition + ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "if (condition)"); + + ImGui::Spacing(); + + // Affichage visuel des branches + ImGui::BeginGroup(); + { + ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), ICON_MDI_CHECK " TRUE"); + ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), ICON_MDI_CLOSE " FALSE"); + } + ImGui::EndGroup(); +} \ No newline at end of file diff --git a/story-editor/src/node_editor/branch_node_widget.h b/story-editor/src/node_editor/branch_node_widget.h new file mode 100644 index 0000000..69d0a5c --- /dev/null +++ b/story-editor/src/node_editor/branch_node_widget.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include "base_node_widget.h" +#include "i_story_manager.h" +#include "i_story_project.h" +#include "gui.h" +#include "branch_node.h" + +/** + * @brief Widget pour le nœud Branch (If/Else) + * + * Gère l'affichage et les propriétés d'un nœud de branchement conditionnel simple. + * La condition est évaluée selon la convention: 0 = false, toute autre valeur = true + * + * Ports: + * - Entrée 0: Flux d'exécution + * - Entrée 1: Condition booléenne (provient généralement d'un OperatorNode) + * - Sortie 0: Branche TRUE (condition != 0) + * - Sortie 1: Branche FALSE (condition == 0) + */ +class BranchNodeWidget : public BaseNodeWidget +{ +public: + BranchNodeWidget(IStoryManager &manager, std::shared_ptr node); + + void Draw() override; + void DrawProperties(std::shared_ptr story) override; + void Initialize() override; + +private: + std::shared_ptr m_branchNode; +}; \ No newline at end of file