diff --git a/core/story-manager/src/compiler/tac.h b/core/story-manager/src/compiler/tac.h index 5e87f5c..6873021 100644 --- a/core/story-manager/src/compiler/tac.h +++ b/core/story-manager/src/compiler/tac.h @@ -324,6 +324,10 @@ private: #include "wait_delay_node.h" #include "play_media_node.h" #include "send_signal_node.h" +#include "for_loop_node.h" +#include "while_loop_node.h" +#include "break_node.h" +#include "continue_node.h" // =================================================================== // SECTION 2: CLASSE TACGenerator - MODIFICATIONS @@ -376,6 +380,16 @@ private: TACProgram m_program; int m_tempCounter; int m_labelCounter; + + // Structure pour le contexte de boucle + struct LoopContext { + std::string startLabel; // Label du début de la boucle + std::string endLabel; // Label de fin de la boucle + std::string continueLabel; // Label pour continue (peut différer du start) + }; + + // Pile de contextes de boucles pour gérer Break/Continue et boucles imbriquées + std::vector m_loopStack; // Map pour garder en mémoire où sont stockés les résultats des nœuds std::map> m_nodeResults; @@ -383,13 +397,38 @@ private: // Set pour éviter de visiter deux fois le même nœud std::set m_visitedNodes; - // NOUVEAU: Variables disponibles pour résolution + // Variables disponibles pour résolution std::vector> m_variables; // =================================================================== // HELPERS // =================================================================== + void GenerateExecutionChain(std::shared_ptr startNode) { + if (!startNode) return; + + // Générer le nœud actuel + GenerateNode(startNode); + + // Suivre la chaîne d'exécution (enfants d'exécution) + for (const auto& child : startNode->children) { + GenerateExecutionChain(child); + } + } + + // Vérifie si on est dans une boucle + bool IsInLoop() const { + return !m_loopStack.empty(); + } + + // Obtient le contexte de la boucle courante + const LoopContext& GetCurrentLoop() const { + if (m_loopStack.empty()) { + throw std::runtime_error("Not in a loop context"); + } + return m_loopStack.back(); + } + // Génère un nouveau temporaire std::shared_ptr NewTemp() { return std::make_shared( @@ -407,7 +446,7 @@ private: } // =================================================================== - // NOUVEAU: HELPERS POUR RÉSOLUTION DE VARIABLES + // HELPERS POUR RÉSOLUTION DE VARIABLES // =================================================================== std::shared_ptr ResolveVariableByUuid(const std::string& uuid) const { @@ -491,6 +530,15 @@ private: std::cout << " -> Type: CallFunctionNode\n"; GenerateCallFunctionNode(node); } + else if (node->IsType()) { + std::cout << " -> Type: FunctionEntryNode (generating children)\n"; + // Entry point, générer les enfants + for (size_t i = 0; i < node->children.size(); i++) { + std::cout << " Processing child [" << i << "]\n"; + GenerateNode(node->children[i]); + } + } + // =================================================================== // NŒUDS SYSCALL // =================================================================== @@ -510,14 +558,31 @@ private: std::cout << " -> Type: SendSignalNode\n"; GenerateSendSignalNode(node); } - else if (node->IsType()) { - std::cout << " -> Type: FunctionEntryNode (generating children)\n"; - // Entry point, générer les enfants - for (size_t i = 0; i < node->children.size(); i++) { - std::cout << " Processing child [" << i << "]\n"; - GenerateNode(node->children[i]); - } + + // =================================================================== + // OUR LES BOUCLES + // =================================================================== + else if (node->IsType()) { + std::cout << " -> Type: ForLoopNode\n"; + GenerateForLoopNode(node); + return nullptr; } + else if (node->IsType()) { + std::cout << " -> Type: WhileLoopNode\n"; + GenerateWhileLoopNode(node); + return nullptr; + } + else if (node->IsType()) { + std::cout << " -> Type: BreakNode\n"; + GenerateBreakNode(node); + return nullptr; + } + else if (node->IsType()) { + std::cout << " -> Type: ContinueNode\n"; + GenerateContinueNode(node); + return nullptr; + } + // Mémoriser le résultat if (result) { @@ -532,6 +597,291 @@ private: // GÉNÉRATION PAR TYPE DE NŒUD - EXISTANTS (pas de changement) // =================================================================== + // =================================================================== + // GÉNÉRATION DU FOR LOOP + // =================================================================== + void GenerateForLoopNode(std::shared_ptr node) { + auto* forLoopNode = node->GetAs(); + if (!forLoopNode) { + throw std::runtime_error("ForLoopNode cast failed"); + } + + std::cout << " Generating ForLoopNode\n"; + + // Créer les labels pour la boucle + auto loopStart = NewLabel("for_start"); + auto loopBody = NewLabel("for_body"); + auto loopContinue = NewLabel("for_continue"); + auto loopEnd = NewLabel("for_end"); + + // ⭐ IMPORTANT : Empiler le contexte AVANT de générer le corps + m_loopStack.push_back({ + loopStart->GetValue(), + loopEnd->GetValue(), + loopContinue->GetValue() + }); + + // === 1. INITIALISATION : index = start === + std::shared_ptr startOperand; + auto startInput = node->GetDataInput(1); + if (startInput) { + startOperand = GenerateNode(startInput); + } else { + startOperand = std::make_shared( + TACOperand::Type::CONSTANT, + std::to_string(forLoopNode->GetStartIndex()) + ); + } + + // Créer la variable temporaire pour l'index + auto indexTemp = NewTemp(); + auto copyInstr = std::make_shared( + TACInstruction::OpCode::COPY, + indexTemp, + startOperand + ); + m_program.AddInstruction(copyInstr); + + // === 2. LABEL loop_start === + auto labelStart = std::make_shared( + TACInstruction::OpCode::LABEL, + loopStart + ); + m_program.AddInstruction(labelStart); + + // === 3. CONDITION : index < end === + std::shared_ptr endOperand; + auto endInput = node->GetDataInput(2); + if (endInput) { + endOperand = GenerateNode(endInput); + } else { + endOperand = std::make_shared( + TACOperand::Type::CONSTANT, + std::to_string(forLoopNode->GetEndIndex()) + ); + } + + // Test: index < end + auto condTemp = NewTemp(); + auto ltInstr = std::make_shared( + TACInstruction::OpCode::LT, + condTemp, + indexTemp, + endOperand + ); + m_program.AddInstruction(ltInstr); + + // if (!condition) goto loop_end + auto ifFalse = std::make_shared( + TACInstruction::OpCode::IF_FALSE, + loopEnd, + condTemp + ); + m_program.AddInstruction(ifFalse); + + // === 4. LABEL loop_body === + auto labelBody = std::make_shared( + TACInstruction::OpCode::LABEL, + loopBody + ); + m_program.AddInstruction(labelBody); + + // === 5. CORPS DE LA BOUCLE - CORRECTION === + // Générer toute la chaîne d'exécution (pas juste le premier enfant) + if (node->GetChild(0)) { + std::cout << " Generating loop body execution chain...\n"; + GenerateExecutionChain(node->GetChild(0)); + } + + // === 6. LABEL loop_continue (pour Continue) === + auto labelContinue = std::make_shared( + TACInstruction::OpCode::LABEL, + loopContinue + ); + m_program.AddInstruction(labelContinue); + + // === 7. INCRÉMENTATION : index = index + step === + std::shared_ptr stepOperand; + auto stepInput = node->GetDataInput(3); + if (stepInput) { + stepOperand = GenerateNode(stepInput); + } else { + stepOperand = std::make_shared( + TACOperand::Type::CONSTANT, + std::to_string(forLoopNode->GetStep()) + ); + } + + auto addInstr = std::make_shared( + TACInstruction::OpCode::ADD, + indexTemp, + indexTemp, + stepOperand + ); + m_program.AddInstruction(addInstr); + + // === 8. RETOUR AU DÉBUT === + auto gotoStart = std::make_shared( + TACInstruction::OpCode::GOTO, + loopStart + ); + m_program.AddInstruction(gotoStart); + + // === 9. LABEL loop_end === + auto labelEnd = std::make_shared( + TACInstruction::OpCode::LABEL, + loopEnd + ); + m_program.AddInstruction(labelEnd); + + // ⭐ IMPORTANT : Dépiler APRÈS avoir généré tout le corps + m_loopStack.pop_back(); + + // === 10. COMPLETED (port de sortie 1) === + if (node->GetChild(1)) { + GenerateNode(node->GetChild(1)); + } + } + + // =================================================================== + // GÉNÉRATION DU WHILE LOOP + // =================================================================== + void GenerateWhileLoopNode(std::shared_ptr node) { + auto* whileLoopNode = node->GetAs(); + if (!whileLoopNode) { + throw std::runtime_error("WhileLoopNode cast failed"); + } + + std::cout << " Generating WhileLoopNode\n"; + + // Créer les labels + auto loopStart = NewLabel("while_start"); + auto loopBody = NewLabel("while_body"); + auto loopEnd = NewLabel("while_end"); + + // ⭐ Empiler le contexte AVANT le corps + m_loopStack.push_back({ + loopStart->GetValue(), + loopEnd->GetValue(), + loopStart->GetValue() + }); + + // === 1. LABEL loop_start === + auto labelStart = std::make_shared( + TACInstruction::OpCode::LABEL, + loopStart + ); + m_program.AddInstruction(labelStart); + + // === 2. ÉVALUER LA CONDITION === + auto conditionInput = node->GetDataInput(1); + if (!conditionInput) { + throw std::runtime_error("WhileLoopNode missing condition input on port 1"); + } + + auto conditionOperand = GenerateNode(conditionInput); + + // === 3. TEST : if (!condition) goto loop_end === + auto ifFalse = std::make_shared( + TACInstruction::OpCode::IF_FALSE, + loopEnd, + conditionOperand + ); + m_program.AddInstruction(ifFalse); + + // === 4. LABEL loop_body === + auto labelBody = std::make_shared( + TACInstruction::OpCode::LABEL, + loopBody + ); + m_program.AddInstruction(labelBody); + + // === 5. CORPS DE LA BOUCLE - CORRECTION === + if (node->GetChild(0)) { + GenerateExecutionChain(node->GetChild(0)); + } + + // === 6. RETOUR AU DÉBUT === + auto gotoStart = std::make_shared( + TACInstruction::OpCode::GOTO, + loopStart + ); + m_program.AddInstruction(gotoStart); + + // === 7. LABEL loop_end === + auto labelEnd = std::make_shared( + TACInstruction::OpCode::LABEL, + loopEnd + ); + m_program.AddInstruction(labelEnd); + + // ⭐ Dépiler APRÈS le corps + m_loopStack.pop_back(); + + // === 8. COMPLETED (port de sortie 1) === + if (node->GetChild(1)) { + GenerateNode(node->GetChild(1)); + } + } + + // =================================================================== + // GÉNÉRATION DU BREAK + // =================================================================== + void GenerateBreakNode(std::shared_ptr node) { + auto* breakNode = node->GetAs(); + if (!breakNode) { + throw std::runtime_error("BreakNode cast failed"); + } + + if (!IsInLoop()) { + throw std::runtime_error("Break statement outside of loop"); + } + + std::cout << " Generating BreakNode (jump to loop end)\n"; + + // Sauter vers la fin de la boucle courante + const auto& loopContext = GetCurrentLoop(); + auto endLabel = std::make_shared( + TACOperand::Type::LABEL, + loopContext.endLabel + ); + + auto gotoEnd = std::make_shared( + TACInstruction::OpCode::GOTO, + endLabel + ); + m_program.AddInstruction(gotoEnd); + } + + // =================================================================== + // GÉNÉRATION DU CONTINUE + // =================================================================== + void GenerateContinueNode(std::shared_ptr node) { + auto* continueNode = node->GetAs(); + if (!continueNode) { + throw std::runtime_error("ContinueNode cast failed"); + } + + if (!IsInLoop()) { + throw std::runtime_error("Continue statement outside of loop"); + } + + std::cout << " Generating ContinueNode (jump to loop continue point)\n"; + + // Sauter vers le point de continue de la boucle courante + const auto& loopContext = GetCurrentLoop(); + auto continueLabel = std::make_shared( + TACOperand::Type::LABEL, + loopContext.continueLabel + ); + + auto gotoContinue = std::make_shared( + TACInstruction::OpCode::GOTO, + continueLabel + ); + m_program.AddInstruction(gotoContinue); + } + std::shared_ptr GenerateVariableNode(std::shared_ptr node) { auto* varNode = node->GetAs(); if (!varNode) { diff --git a/core/story-manager/src/nodes/break_node.cpp b/core/story-manager/src/nodes/break_node.cpp new file mode 100644 index 0000000..3bb9538 --- /dev/null +++ b/core/story-manager/src/nodes/break_node.cpp @@ -0,0 +1,24 @@ +#include "break_node.h" + +BreakNode::BreakNode(const std::string &type) + : BaseNode(type, "Break") +{ + SetBehavior(Behavior::BEHAVIOR_EXECUTION); + + // Port d'entrée 0: Exécution (EXECUTION_PORT) + AddInputPort(Port::Type::EXECUTION_PORT, ">", true); + + // Pas de port de sortie : le flux est redirigé vers la fin de la boucle + // par le générateur TAC + + // Initialiser les données internes + nlohmann::json j; + j["node_type"] = "break"; + SetInternalData(j); +} + +void BreakNode::Initialize() +{ + // Charger les données sauvegardées si nécessaire + nlohmann::json j = GetInternalData(); +} diff --git a/core/story-manager/src/nodes/break_node.h b/core/story-manager/src/nodes/break_node.h new file mode 100644 index 0000000..acedef7 --- /dev/null +++ b/core/story-manager/src/nodes/break_node.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "base_node.h" + +class BreakNode : public BaseNode +{ +public: + BreakNode(const std::string &type = "break-node"); + + void Initialize() override; +}; diff --git a/core/story-manager/src/nodes/continue_node.cpp b/core/story-manager/src/nodes/continue_node.cpp new file mode 100644 index 0000000..469b15c --- /dev/null +++ b/core/story-manager/src/nodes/continue_node.cpp @@ -0,0 +1,24 @@ +#include "continue_node.h" + +ContinueNode::ContinueNode(const std::string &type) + : BaseNode(type, "Continue") +{ + SetBehavior(Behavior::BEHAVIOR_EXECUTION); + + // Port d'entrée 0: Exécution (EXECUTION_PORT) + AddInputPort(Port::Type::EXECUTION_PORT, ">", true); + + // Pas de port de sortie : le flux est redirigé vers le début de la boucle + // par le générateur TAC + + // Initialiser les données internes + nlohmann::json j; + j["node_type"] = "continue"; + SetInternalData(j); +} + +void ContinueNode::Initialize() +{ + // Charger les données sauvegardées si nécessaire + nlohmann::json j = GetInternalData(); +} \ No newline at end of file diff --git a/core/story-manager/src/nodes/continue_node.h b/core/story-manager/src/nodes/continue_node.h new file mode 100644 index 0000000..3eecd61 --- /dev/null +++ b/core/story-manager/src/nodes/continue_node.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "base_node.h" + +class ContinueNode : public BaseNode +{ +public: + ContinueNode(const std::string &type = "continue-node"); + + void Initialize() override; +}; \ No newline at end of file diff --git a/core/story-manager/src/nodes/for_loop_node.cpp b/core/story-manager/src/nodes/for_loop_node.cpp new file mode 100644 index 0000000..81df72e --- /dev/null +++ b/core/story-manager/src/nodes/for_loop_node.cpp @@ -0,0 +1,46 @@ +#include "for_loop_node.h" + +ForLoopNode::ForLoopNode(const std::string &type) + : BaseNode(type, "For Loop") +{ + SetBehavior(Behavior::BEHAVIOR_EXECUTION); + + // Port d'entrée 0: Exécution (EXECUTION_PORT) + AddInputPort(Port::Type::EXECUTION_PORT, ">", true); + + // Ports d'entrée pour les données + AddInputPort(Port::Type::DATA_PORT, "start"); // Port 1 + AddInputPort(Port::Type::DATA_PORT, "end"); // Port 2 + AddInputPort(Port::Type::DATA_PORT, "step"); // Port 3 + + // Ports de sortie pour l'exécution + AddOutputPort(Port::Type::EXECUTION_PORT, "body", true); // Port 0 + AddOutputPort(Port::Type::EXECUTION_PORT, "done", true); // Port 1 + + // Port de sortie pour l'index courant + AddOutputPort(Port::Type::DATA_PORT, "index"); // Port 2 + + // Initialiser les données internes + nlohmann::json j; + j["start_index"] = m_startIndex; + j["end_index"] = m_endIndex; + j["step"] = m_step; + j["node_type"] = "for_loop"; + SetInternalData(j); +} + +void ForLoopNode::Initialize() +{ + // Charger les données sauvegardées + nlohmann::json j = GetInternalData(); + + if (j.contains("start_index")) { + m_startIndex = j["start_index"].get(); + } + if (j.contains("end_index")) { + m_endIndex = j["end_index"].get(); + } + if (j.contains("step")) { + m_step = j["step"].get(); + } +} diff --git a/core/story-manager/src/nodes/for_loop_node.h b/core/story-manager/src/nodes/for_loop_node.h new file mode 100644 index 0000000..bff33fe --- /dev/null +++ b/core/story-manager/src/nodes/for_loop_node.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include "base_node.h" + +class ForLoopNode : public BaseNode +{ +public: + ForLoopNode(const std::string &type = "for-loop-node"); + + void Initialize() override; + + // Propriétés configurables (valeurs par défaut si non connectées) + int GetStartIndex() const { return m_startIndex; } + void SetStartIndex(int value) { + m_startIndex = value; + nlohmann::json j = GetInternalData(); + j["start_index"] = m_startIndex; + SetInternalData(j); + } + + int GetEndIndex() const { return m_endIndex; } + void SetEndIndex(int value) { + m_endIndex = value; + nlohmann::json j = GetInternalData(); + j["end_index"] = m_endIndex; + SetInternalData(j); + } + + int GetStep() const { return m_step; } + void SetStep(int value) { + m_step = value; + nlohmann::json j = GetInternalData(); + j["step"] = m_step; + SetInternalData(j); + } + +private: + int m_startIndex{0}; + int m_endIndex{10}; + int m_step{1}; +}; \ No newline at end of file diff --git a/core/story-manager/src/nodes/while_loop_node.cpp b/core/story-manager/src/nodes/while_loop_node.cpp new file mode 100644 index 0000000..1a6046a --- /dev/null +++ b/core/story-manager/src/nodes/while_loop_node.cpp @@ -0,0 +1,28 @@ +#include "while_loop_node.h" + +WhileLoopNode::WhileLoopNode(const std::string &type) + : BaseNode(type, "While Loop") +{ + SetBehavior(Behavior::BEHAVIOR_EXECUTION); + + // Port d'entrée 0: Exécution (EXECUTION_PORT) + AddInputPort(Port::Type::EXECUTION_PORT, ">", true); + + // Port d'entrée 1: Condition (DATA_PORT) + AddInputPort(Port::Type::DATA_PORT, "condition"); + + // Ports de sortie pour l'exécution + AddOutputPort(Port::Type::EXECUTION_PORT, "body", true); // Port 0 + AddOutputPort(Port::Type::EXECUTION_PORT, "done", true); // Port 1 + + // Initialiser les données internes + nlohmann::json j; + j["node_type"] = "while_loop"; + SetInternalData(j); +} + +void WhileLoopNode::Initialize() +{ + // Charger les données sauvegardées si nécessaire + nlohmann::json j = GetInternalData(); +} diff --git a/core/story-manager/src/nodes/while_loop_node.h b/core/story-manager/src/nodes/while_loop_node.h new file mode 100644 index 0000000..83246ff --- /dev/null +++ b/core/story-manager/src/nodes/while_loop_node.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include "base_node.h" + +class WhileLoopNode : public BaseNode +{ +public: + WhileLoopNode(const std::string &type = "while-loop-node"); + + void Initialize() override; +}; diff --git a/core/story-manager/src/nodes_factory.h b/core/story-manager/src/nodes_factory.h index b1ed0de..9f1dce2 100644 --- a/core/story-manager/src/nodes_factory.h +++ b/core/story-manager/src/nodes_factory.h @@ -20,6 +20,10 @@ #include "wait_event_node.h" #include "play_media_node.h" #include "send_signal_node.h" +#include "for_loop_node.h" +#include "while_loop_node.h" +#include "break_node.h" +#include "continue_node.h" static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec"; static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb"; @@ -31,6 +35,10 @@ static const std::string WaitEventNodeUuid = "02225cff-4975-400e-8130-41524d8af7 static const std::string WaitDelayNodeUuid = "02455ef0-4975-4546-94de-720cae6baae3"; static const std::string PlayMediaNodeUuid = "0285e90a-2eb7-4605-baa9-b3712a14dff8"; static const std::string SendSignalNodeUuid = "02c2ce4b-8783-47cb-a55f-90056bebd64b"; +static const std::string ForLoopNodeUuid = "02a1b2c3-4d5e-6f7a-8b9c-0d1e2f3a4b5c"; +static const std::string WhileLoopNodeUuid = "02b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d"; +static const std::string BreakNodeUuid = "02c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e"; +static const std::string ContinueNodeUuid = "02d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f"; typedef std::shared_ptr (*GenericCreator)(const std::string &type); @@ -54,6 +62,10 @@ public: registerNode(WaitDelayNodeUuid, std::make_shared("Wait Delay")); registerNode(PlayMediaNodeUuid, std::make_shared("Play Media")); registerNode(SendSignalNodeUuid, std::make_shared("Send Signal")); + registerNode(ForLoopNodeUuid, std::make_shared("For Loop")); + registerNode(WhileLoopNodeUuid, std::make_shared("While Loop")); + registerNode(BreakNodeUuid, std::make_shared("Break")); + registerNode(ContinueNodeUuid, std::make_shared("Continue")); } ~NodesFactory() = default; diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index d120ed2..3e85b47 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(${PROJECT_NAME} test_ast.cpp test_print_node.cpp test_branch.cpp + test_loops.cpp ../story-manager/src/nodes/base_node.cpp ../story-manager/src/nodes/branch_node.cpp @@ -41,6 +42,10 @@ add_executable(${PROJECT_NAME} ../story-manager/src/nodes/wait_delay_node.cpp ../story-manager/src/nodes/play_media_node.cpp ../story-manager/src/nodes/send_signal_node.cpp + ../story-manager/src/nodes/for_loop_node.cpp + ../story-manager/src/nodes/while_loop_node.cpp + ../story-manager/src/nodes/break_node.cpp + ../story-manager/src/nodes/continue_node.cpp ../chip32/chip32_assembler.cpp diff --git a/core/tests/test_loops.cpp b/core/tests/test_loops.cpp new file mode 100644 index 0000000..567dd15 --- /dev/null +++ b/core/tests/test_loops.cpp @@ -0,0 +1,417 @@ +// =================================================================== +// core/tests/test_loops.cpp +// Tests unitaires pour ForLoop, WhileLoop, Break et Continue +// =================================================================== + +#include +#include "for_loop_node.h" +#include "while_loop_node.h" +#include "break_node.h" +#include "continue_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 : ForLoopNode simple (0 à 5) +// =================================================================== +TEST_CASE("Simple ForLoop counting 0 to 5", "[loop][for]") { + std::cout << "\n=== Test: Simple ForLoop 0 to 5 ===\n"; + + std::vector> variables; + + // Nodes + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto forLoop = std::make_shared("for-loop"); + forLoop->SetStartIndex(0); + forLoop->SetEndIndex(5); + forLoop->SetStep(1); + forLoop->Initialize(); + + auto printNode = std::make_shared("print-loop"); + printNode->SetText("Iteration"); + printNode->Initialize(); + + auto printEnd = std::make_shared("print-end"); + printEnd->SetText("Loop completed!"); + printEnd->Initialize(); + + std::vector> nodes = { + functionEntry, + forLoop, + printNode, + printEnd + }; + + // Connections + std::vector> connections; + + // Execution: Entry → ForLoop + auto execConn1 = std::make_shared(); + execConn1->outNodeId = functionEntry->GetId(); + execConn1->outPortIndex = 0; + execConn1->inNodeId = forLoop->GetId(); + execConn1->inPortIndex = 0; + execConn1->type = Connection::EXECUTION_LINK; + connections.push_back(execConn1); + + // Execution: ForLoop.body → Print + auto execConn2 = std::make_shared(); + execConn2->outNodeId = forLoop->GetId(); + execConn2->outPortIndex = 0; // Loop Body + execConn2->inNodeId = printNode->GetId(); + execConn2->inPortIndex = 0; + execConn2->type = Connection::EXECUTION_LINK; + connections.push_back(execConn2); + + // Execution: ForLoop.completed → PrintEnd + auto execConn3 = std::make_shared(); + execConn3->outNodeId = forLoop->GetId(); + execConn3->outPortIndex = 1; // Completed + execConn3->inNodeId = printEnd->GetId(); + execConn3->inPortIndex = 0; + execConn3->type = Connection::EXECUTION_LINK; + connections.push_back(execConn3); + + // Build AST + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + // Generate Assembly + AssemblyGenerator::GeneratorContext context( + variables, + "2025-01-20 10:00:00", + "test-for-loop", + 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("for_start") != std::string::npos); + REQUIRE(assembly.find("for_end") != std::string::npos); + + // Execute + std::cout << "\n--- Execution Output ---\n"; + std::cout << "Expected: 5 iterations + completion message\n"; + + Chip32::Machine machine; + machine.QuickExecute(assembly); + + std::cout << "\n✓ Test passed\n"; +} + +// =================================================================== +// TEST 2 : WhileLoop avec condition variable +// =================================================================== +TEST_CASE("WhileLoop with variable condition", "[loop][while]") { + std::cout << "\n=== Test: WhileLoop with condition ===\n"; + + std::vector> variables; + + auto varCounter = std::make_shared("counter"); + varCounter->SetIntegerValue(0); + variables.push_back(varCounter); + + auto varLimit = std::make_shared("limit"); + varLimit->SetIntegerValue(3); + variables.push_back(varLimit); + + // Nodes + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto varNodeCounter = std::make_shared("var-counter"); + varNodeCounter->SetVariable(varCounter); + + auto varNodeLimit = std::make_shared("var-limit"); + varNodeLimit->SetVariable(varLimit); + + // Condition: counter < limit + auto compareNode = std::make_shared("compare-lt"); + compareNode->SetOperationType(OperatorNode::OperationType::LESS_THAN); + compareNode->Initialize(); + + auto whileLoop = std::make_shared("while-loop"); + whileLoop->Initialize(); + + auto printNode = std::make_shared("print-loop"); + printNode->SetText("Counter value"); + printNode->Initialize(); + + auto printEnd = std::make_shared("print-end"); + printEnd->SetText("While loop completed!"); + printEnd->Initialize(); + + std::vector> nodes = { + functionEntry, + varNodeCounter, + varNodeLimit, + compareNode, + whileLoop, + printNode, + printEnd + }; + + // Connections + std::vector> connections; + + // Execution: Entry → While + auto execConn1 = std::make_shared(); + execConn1->outNodeId = functionEntry->GetId(); + execConn1->outPortIndex = 0; + execConn1->inNodeId = whileLoop->GetId(); + execConn1->inPortIndex = 0; + execConn1->type = Connection::EXECUTION_LINK; + connections.push_back(execConn1); + + // Data: counter → compare.in1 + auto dataConn1 = std::make_shared(); + dataConn1->outNodeId = varNodeCounter->GetId(); + dataConn1->outPortIndex = 0; + dataConn1->inNodeId = compareNode->GetId(); + dataConn1->inPortIndex = 0; + dataConn1->type = Connection::DATA_LINK; + connections.push_back(dataConn1); + + // Data: limit → compare.in2 + auto dataConn2 = std::make_shared(); + dataConn2->outNodeId = varNodeLimit->GetId(); + dataConn2->outPortIndex = 0; + dataConn2->inNodeId = compareNode->GetId(); + dataConn2->inPortIndex = 1; + dataConn2->type = Connection::DATA_LINK; + connections.push_back(dataConn2); + + // Data: compare.out → while.condition + auto dataConn3 = std::make_shared(); + dataConn3->outNodeId = compareNode->GetId(); + dataConn3->outPortIndex = 0; + dataConn3->inNodeId = whileLoop->GetId(); + dataConn3->inPortIndex = 1; + dataConn3->type = Connection::DATA_LINK; + connections.push_back(dataConn3); + + // Execution: While.body → Print + auto execConn2 = std::make_shared(); + execConn2->outNodeId = whileLoop->GetId(); + execConn2->outPortIndex = 0; + execConn2->inNodeId = printNode->GetId(); + execConn2->inPortIndex = 0; + execConn2->type = Connection::EXECUTION_LINK; + connections.push_back(execConn2); + + // Execution: While.completed → PrintEnd + auto execConn3 = std::make_shared(); + execConn3->outNodeId = whileLoop->GetId(); + execConn3->outPortIndex = 1; + execConn3->inNodeId = printEnd->GetId(); + execConn3->inPortIndex = 0; + execConn3->type = Connection::EXECUTION_LINK; + connections.push_back(execConn3); + + // Build and test + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + AssemblyGenerator::GeneratorContext context( + variables, "2025-01-20", "test-while-loop", 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; + + REQUIRE(assembly.find("while_start") != std::string::npos); + REQUIRE(assembly.find("while_end") != std::string::npos); + + std::cout << "\n✓ Test passed\n"; +} + +// =================================================================== +// TEST 3 : ForLoop avec Break +// =================================================================== +TEST_CASE("ForLoop with Break at first iteration", "[loop][for][break]") { + std::cout << "\n=== Test: ForLoop with Break ===\n"; + + std::vector> variables; + + // Nodes + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto forLoop = std::make_shared("for-loop"); + forLoop->SetStartIndex(0); + forLoop->SetEndIndex(10); + forLoop->SetStep(1); + forLoop->Initialize(); + + auto printNode = std::make_shared("print-iter"); + printNode->SetText("Iteration"); + printNode->Initialize(); + + auto breakNode = std::make_shared("break-node"); + breakNode->Initialize(); + + auto printEnd = std::make_shared("print-end"); + printEnd->SetText("Broke out of loop"); + printEnd->Initialize(); + + std::vector> nodes = { + functionEntry, + forLoop, + printNode, + breakNode, + printEnd + }; + + // Connections + std::vector> connections; + + // Entry → ForLoop + auto execConn1 = std::make_shared(); + execConn1->outNodeId = functionEntry->GetId(); + execConn1->outPortIndex = 0; + execConn1->inNodeId = forLoop->GetId(); + execConn1->inPortIndex = 0; + execConn1->type = Connection::EXECUTION_LINK; + connections.push_back(execConn1); + + // ForLoop.body → Print + auto execConn2 = std::make_shared(); + execConn2->outNodeId = forLoop->GetId(); + execConn2->outPortIndex = 0; + execConn2->inNodeId = printNode->GetId(); + execConn2->inPortIndex = 0; + execConn2->type = Connection::EXECUTION_LINK; + connections.push_back(execConn2); + + // Print → Break + auto execConn3 = std::make_shared(); + execConn3->outNodeId = printNode->GetId(); + execConn3->outPortIndex = 0; + execConn3->inNodeId = breakNode->GetId(); + execConn3->inPortIndex = 0; + execConn3->type = Connection::EXECUTION_LINK; + connections.push_back(execConn3); + + // ForLoop.completed → PrintEnd + auto execConn4 = std::make_shared(); + execConn4->outNodeId = forLoop->GetId(); + execConn4->outPortIndex = 1; + execConn4->inNodeId = printEnd->GetId(); + execConn4->inPortIndex = 0; + execConn4->type = Connection::EXECUTION_LINK; + connections.push_back(execConn4); + + // Build and generate + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + AssemblyGenerator::GeneratorContext context( + variables, "2025-01-20", "test-break", 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; + + REQUIRE(assembly.find("for_end") != std::string::npos); + REQUIRE(assembly.find("jump") != std::string::npos); + + std::cout << "\n✓ Test passed\n"; +} + +// =================================================================== +// TEST 4 : ForLoop avec Continue +// =================================================================== +TEST_CASE("ForLoop with Continue", "[loop][for][continue]") { + std::cout << "\n=== Test: ForLoop with Continue ===\n"; + + std::vector> variables; + + auto functionEntry = std::make_shared("function-entry"); + functionEntry->SetWeight(100); + + auto forLoop = std::make_shared("for-loop"); + forLoop->SetStartIndex(0); + forLoop->SetEndIndex(5); + forLoop->Initialize(); + + auto continueNode = std::make_shared("continue-node"); + continueNode->Initialize(); + + auto printNode = std::make_shared("print-odd"); + printNode->SetText("Should not print"); + printNode->Initialize(); + + std::vector> nodes = { + functionEntry, + forLoop, + continueNode, + printNode + }; + + std::vector> connections; + + // Entry → ForLoop + auto execConn1 = std::make_shared(); + execConn1->outNodeId = functionEntry->GetId(); + execConn1->outPortIndex = 0; + execConn1->inNodeId = forLoop->GetId(); + execConn1->inPortIndex = 0; + execConn1->type = Connection::EXECUTION_LINK; + connections.push_back(execConn1); + + // ForLoop.body → Continue + auto execConn2 = std::make_shared(); + execConn2->outNodeId = forLoop->GetId(); + execConn2->outPortIndex = 0; + execConn2->inNodeId = continueNode->GetId(); + execConn2->inPortIndex = 0; + execConn2->type = Connection::EXECUTION_LINK; + connections.push_back(execConn2); + + ASTBuilder builder(nodes, connections); + auto pathTree = builder.BuildAST(); + + AssemblyGenerator::GeneratorContext context( + variables, "2025-01-20", "test-continue", 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; + + REQUIRE(assembly.find("for_continue") != 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 a150b9c..60ca59b 100644 --- a/story-editor/CMakeLists.txt +++ b/story-editor/CMakeLists.txt @@ -152,6 +152,10 @@ set(SRCS src/node_editor/wait_delay_node_widget.cpp src/node_editor/play_media_node_widget.cpp src/node_editor/send_signal_node_widget.cpp + src/node_editor/for_loop_node_widget.cpp + src/node_editor/while_loop_node_widget.cpp + src/node_editor/break_node_widget.cpp + src/node_editor/continue_node_widget.cpp src/gui.cpp src/media_converter.cpp @@ -193,6 +197,10 @@ set(SRCS ../core/story-manager/src/nodes/wait_delay_node.cpp ../core/story-manager/src/nodes/play_media_node.cpp ../core/story-manager/src/nodes/send_signal_node.cpp + ../core/story-manager/src/nodes/for_loop_node.cpp + ../core/story-manager/src/nodes/while_loop_node.cpp + ../core/story-manager/src/nodes/break_node.cpp + ../core/story-manager/src/nodes/continue_node.cpp ../core/story-manager/src/nodes/connection.cpp ../core/story-manager/src/story_db.cpp diff --git a/story-editor/imgui.ini b/story-editor/imgui.ini index 8d6b23d..b42a55d 100644 --- a/story-editor/imgui.ini +++ b/story-editor/imgui.ini @@ -1,6 +1,6 @@ [Window][WindowOverViewport_11111111] Pos=60,26 -Size=1220,694 +Size=1860,982 Collapsed=0 [Window][Debug##Default] @@ -9,32 +9,32 @@ Size=400,400 Collapsed=0 [Window][Library Manager] -Pos=710,26 -Size=570,469 +Pos=1060,26 +Size=860,653 Collapsed=0 DockId=0x00000002,0 [Window][Console] -Pos=60,497 -Size=527,223 +Pos=60,681 +Size=805,327 Collapsed=0 DockId=0x00000004,0 [Window][Emulator] -Pos=710,26 -Size=570,469 +Pos=1060,26 +Size=860,653 Collapsed=0 DockId=0x00000002,5 [Window][Code viewer] -Pos=710,26 -Size=570,469 +Pos=1060,26 +Size=860,653 Collapsed=0 DockId=0x00000002,4 [Window][Resources] -Pos=710,26 -Size=570,469 +Pos=1060,26 +Size=860,653 Collapsed=0 DockId=0x00000002,1 @@ -50,36 +50,36 @@ Size=150,42 Collapsed=0 [Window][Variables] -Pos=589,497 -Size=691,223 +Pos=867,681 +Size=1053,327 Collapsed=0 DockId=0x00000005,0 [Window][CPU] -Pos=710,26 -Size=570,469 +Pos=1060,26 +Size=860,653 Collapsed=0 DockId=0x00000002,2 [Window][RAM view] -Pos=710,26 -Size=570,469 +Pos=1060,26 +Size=860,653 Collapsed=0 DockId=0x00000002,3 [Window][Properties] -Pos=589,497 -Size=691,223 +Pos=867,681 +Size=1053,327 Collapsed=0 DockId=0x00000005,1 [Window][ToolBar] Pos=0,26 -Size=60,694 +Size=60,982 Collapsed=0 [Window][QuitConfirm] -Pos=508,312 +Pos=828,456 Size=264,96 Collapsed=0 @@ -90,13 +90,13 @@ Collapsed=0 [Window][Module editor] Pos=60,26 -Size=648,469 +Size=998,653 Collapsed=0 DockId=0x00000001,0 [Window][Story editor] Pos=60,26 -Size=648,469 +Size=998,653 Collapsed=0 DockId=0x00000001,1 @@ -106,8 +106,8 @@ Size=687,422 Collapsed=0 [Window][Error List] -Pos=60,497 -Size=527,223 +Pos=60,681 +Size=805,327 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,694 Split=Y - DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,469 Split=X - DockNode ID=0x00000001 Parent=0x00000007 SizeRef=648,694 CentralNode=1 Selected=0x93ADCAAB - DockNode ID=0x00000002 Parent=0x00000007 SizeRef=570,694 Selected=0x63869CAF - DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,223 Split=X Selected=0xEA83D666 +DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1860,982 Split=Y + DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,365 Split=X + DockNode ID=0x00000001 Parent=0x00000007 SizeRef=998,694 CentralNode=1 Selected=0x93ADCAAB + DockNode ID=0x00000002 Parent=0x00000007 SizeRef=860,694 Selected=0x4B07C626 + DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,327 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 1e9d5e6..975bbed 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -38,6 +38,10 @@ #include "send_signal_node_widget.h" #include "wait_delay_node_widget.h" #include "wait_event_node_widget.h" +#include "for_loop_node_widget.h" +#include "while_loop_node_widget.h" +#include "break_node_widget.h" +#include "continue_node_widget.h" MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController) : m_logger(logger) @@ -72,6 +76,10 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo m_widgetFactory.registerNode(WaitDelayNodeUuid); m_widgetFactory.registerNode(PlayMediaNodeUuid); m_widgetFactory.registerNode(SendSignalNodeUuid); + m_widgetFactory.registerNode(ForLoopNodeUuid); + m_widgetFactory.registerNode(WhileLoopNodeUuid); + m_widgetFactory.registerNode(BreakNodeUuid); + m_widgetFactory.registerNode(ContinueNodeUuid); m_eventBus.Subscribe([this](const OpenProjectEvent &event) { OpenProject(event.GetUuid()); diff --git a/story-editor/src/node_editor/break_node_widget.cpp b/story-editor/src/node_editor/break_node_widget.cpp new file mode 100644 index 0000000..1917e85 --- /dev/null +++ b/story-editor/src/node_editor/break_node_widget.cpp @@ -0,0 +1,24 @@ +#include "break_node_widget.h" +#include "imgui.h" +#include "IconsMaterialDesignIcons.h" + +BreakNodeWidget::BreakNodeWidget(IStoryManager &manager, std::shared_ptr base) + : BaseNodeWidget(manager, base) +{ + m_breakNode = std::dynamic_pointer_cast(base); +} + +void BreakNodeWidget::Draw() +{ + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.4f, 0.4f, 1.0f)); + ImGui::Text(ICON_MDI_STOP " Break"); + ImGui::PopStyleColor(); +} + +void BreakNodeWidget::DrawProperties(std::shared_ptr story) +{ + ImGui::Text("Break Statement"); + ImGui::Separator(); + ImGui::TextWrapped("Exits the current loop immediately."); + ImGui::TextWrapped("Must be placed inside a For or While loop."); +} diff --git a/story-editor/src/node_editor/break_node_widget.h b/story-editor/src/node_editor/break_node_widget.h new file mode 100644 index 0000000..a02ee67 --- /dev/null +++ b/story-editor/src/node_editor/break_node_widget.h @@ -0,0 +1,17 @@ +#pragma once + +#include "base_node_widget.h" +#include "break_node.h" + +class BreakNodeWidget : public BaseNodeWidget +{ +public: + BreakNodeWidget(IStoryManager &manager, std::shared_ptr base); + virtual ~BreakNodeWidget() = default; + + void Draw() override; + void DrawProperties(std::shared_ptr story) override; + +private: + std::shared_ptr m_breakNode; +}; diff --git a/story-editor/src/node_editor/continue_node_widget.cpp b/story-editor/src/node_editor/continue_node_widget.cpp new file mode 100644 index 0000000..4b62fb2 --- /dev/null +++ b/story-editor/src/node_editor/continue_node_widget.cpp @@ -0,0 +1,24 @@ +#include "continue_node_widget.h" +#include "imgui.h" +#include "IconsMaterialDesignIcons.h" + +ContinueNodeWidget::ContinueNodeWidget(IStoryManager &manager, std::shared_ptr base) + : BaseNodeWidget(manager, base) +{ + m_continueNode = std::dynamic_pointer_cast(base); +} + +void ContinueNodeWidget::Draw() +{ + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 1.0f, 0.4f, 1.0f)); + ImGui::Text(ICON_MDI_SKIP_NEXT " Continue"); + ImGui::PopStyleColor(); +} + +void ContinueNodeWidget::DrawProperties(std::shared_ptr story) +{ + ImGui::Text("Continue Statement"); + ImGui::Separator(); + ImGui::TextWrapped("Skips to the next iteration of the current loop."); + ImGui::TextWrapped("Must be placed inside a For or While loop."); +} \ No newline at end of file diff --git a/story-editor/src/node_editor/continue_node_widget.h b/story-editor/src/node_editor/continue_node_widget.h new file mode 100644 index 0000000..4be792d --- /dev/null +++ b/story-editor/src/node_editor/continue_node_widget.h @@ -0,0 +1,17 @@ +#pragma once + +#include "base_node_widget.h" +#include "continue_node.h" + +class ContinueNodeWidget : public BaseNodeWidget +{ +public: + ContinueNodeWidget(IStoryManager &manager, std::shared_ptr base); + virtual ~ContinueNodeWidget() = default; + + void Draw() override; + void DrawProperties(std::shared_ptr story) override; + +private: + std::shared_ptr m_continueNode; +}; diff --git a/story-editor/src/node_editor/for_loop_node_widget.cpp b/story-editor/src/node_editor/for_loop_node_widget.cpp new file mode 100644 index 0000000..47b4063 --- /dev/null +++ b/story-editor/src/node_editor/for_loop_node_widget.cpp @@ -0,0 +1,51 @@ +// story-editor/src/node_editor/for_loop_node_widget.cpp +#include "for_loop_node_widget.h" +#include "imgui.h" +#include "IconsMaterialDesignIcons.h" + +ForLoopNodeWidget::ForLoopNodeWidget(IStoryManager &manager, std::shared_ptr base) + : BaseNodeWidget(manager, base) +{ + m_forLoopNode = std::dynamic_pointer_cast(base); +} + +void ForLoopNodeWidget::Draw() +{ + // Affichage simple du nœud dans l'éditeur + ImGui::Text(ICON_MDI_SYNC " For Loop"); + + // Afficher les valeurs configurées (si non connectées) + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f)); + ImGui::TextWrapped("Start: %d, End: %d, Step: %d", + m_forLoopNode->GetStartIndex(), + m_forLoopNode->GetEndIndex(), + m_forLoopNode->GetStep()); + ImGui::PopStyleColor(); +} + +void ForLoopNodeWidget::DrawProperties(std::shared_ptr story) +{ + ImGui::Text("For Loop Properties"); + ImGui::Separator(); + + // Configuration des valeurs par défaut + int start = m_forLoopNode->GetStartIndex(); + if (ImGui::InputInt("Start Index", &start)) { + m_forLoopNode->SetStartIndex(start); + } + + int end = m_forLoopNode->GetEndIndex(); + if (ImGui::InputInt("End Index", &end)) { + m_forLoopNode->SetEndIndex(end); + } + + int step = m_forLoopNode->GetStep(); + if (ImGui::InputInt("Step", &step)) { + if (step != 0) { // Éviter division par zéro + m_forLoopNode->SetStep(step); + } + } + + ImGui::Separator(); + ImGui::TextWrapped("Note: These values are used only if the corresponding input ports are not connected."); +} diff --git a/story-editor/src/node_editor/for_loop_node_widget.h b/story-editor/src/node_editor/for_loop_node_widget.h new file mode 100644 index 0000000..bdc1379 --- /dev/null +++ b/story-editor/src/node_editor/for_loop_node_widget.h @@ -0,0 +1,18 @@ +// story-editor/src/node_editor/for_loop_node_widget.h +#pragma once + +#include "base_node_widget.h" +#include "for_loop_node.h" + +class ForLoopNodeWidget : public BaseNodeWidget +{ +public: + ForLoopNodeWidget(IStoryManager &manager, std::shared_ptr base); + virtual ~ForLoopNodeWidget() = default; + + void Draw() override; + void DrawProperties(std::shared_ptr story) override; + +private: + std::shared_ptr m_forLoopNode; +}; \ No newline at end of file diff --git a/story-editor/src/node_editor/while_loop_node_widget.cpp b/story-editor/src/node_editor/while_loop_node_widget.cpp new file mode 100644 index 0000000..fc335c9 --- /dev/null +++ b/story-editor/src/node_editor/while_loop_node_widget.cpp @@ -0,0 +1,30 @@ +// story-editor/src/node_editor/while_loop_node_widget.cpp +#include "while_loop_node_widget.h" +#include "imgui.h" +#include "IconsMaterialDesignIcons.h" + +WhileLoopNodeWidget::WhileLoopNodeWidget(IStoryManager &manager, std::shared_ptr base) + : BaseNodeWidget(manager, base) +{ + m_whileLoopNode = std::dynamic_pointer_cast(base); +} + +void WhileLoopNodeWidget::Draw() +{ + // Affichage simple + ImGui::Text(ICON_MDI_REPEAT " While Loop"); + + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f)); + ImGui::TextWrapped("Loops while condition is true"); + ImGui::PopStyleColor(); +} + +void WhileLoopNodeWidget::DrawProperties(std::shared_ptr story) +{ + ImGui::Text("While Loop Properties"); + ImGui::Separator(); + + ImGui::TextWrapped("The loop executes as long as the condition input evaluates to true (non-zero)."); + ImGui::Spacing(); + ImGui::TextWrapped("Warning: Ensure the condition can become false to avoid infinite loops."); +} \ No newline at end of file diff --git a/story-editor/src/node_editor/while_loop_node_widget.h b/story-editor/src/node_editor/while_loop_node_widget.h new file mode 100644 index 0000000..4bdb3a9 --- /dev/null +++ b/story-editor/src/node_editor/while_loop_node_widget.h @@ -0,0 +1,18 @@ +// story-editor/src/node_editor/while_loop_node_widget.h +#pragma once + +#include "base_node_widget.h" +#include "while_loop_node.h" + +class WhileLoopNodeWidget : public BaseNodeWidget +{ +public: + WhileLoopNodeWidget(IStoryManager &manager, std::shared_ptr base); + virtual ~WhileLoopNodeWidget() = default; + + void Draw() override; + void DrawProperties(std::shared_ptr story) override; + +private: + std::shared_ptr m_whileLoopNode; +}; \ No newline at end of file