mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Add loops nodes !
This commit is contained in:
parent
c29d099ec9
commit
dc11cb33dd
23 changed files with 1238 additions and 39 deletions
|
|
@ -324,6 +324,10 @@ private:
|
||||||
#include "wait_delay_node.h"
|
#include "wait_delay_node.h"
|
||||||
#include "play_media_node.h"
|
#include "play_media_node.h"
|
||||||
#include "send_signal_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
|
// SECTION 2: CLASSE TACGenerator - MODIFICATIONS
|
||||||
|
|
@ -376,6 +380,16 @@ private:
|
||||||
TACProgram m_program;
|
TACProgram m_program;
|
||||||
int m_tempCounter;
|
int m_tempCounter;
|
||||||
int m_labelCounter;
|
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<LoopContext> m_loopStack;
|
||||||
|
|
||||||
// Map pour garder en mémoire où sont stockés les résultats des nœuds
|
// Map pour garder en mémoire où sont stockés les résultats des nœuds
|
||||||
std::map<std::string, std::shared_ptr<TACOperand>> m_nodeResults;
|
std::map<std::string, std::shared_ptr<TACOperand>> m_nodeResults;
|
||||||
|
|
@ -383,13 +397,38 @@ private:
|
||||||
// Set pour éviter de visiter deux fois le même nœud
|
// Set pour éviter de visiter deux fois le même nœud
|
||||||
std::set<std::string> m_visitedNodes;
|
std::set<std::string> m_visitedNodes;
|
||||||
|
|
||||||
// NOUVEAU: Variables disponibles pour résolution
|
// Variables disponibles pour résolution
|
||||||
std::vector<std::shared_ptr<Variable>> m_variables;
|
std::vector<std::shared_ptr<Variable>> m_variables;
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// HELPERS
|
// HELPERS
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
|
void GenerateExecutionChain(std::shared_ptr<ASTNode> 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
|
// Génère un nouveau temporaire
|
||||||
std::shared_ptr<TACOperand> NewTemp() {
|
std::shared_ptr<TACOperand> NewTemp() {
|
||||||
return std::make_shared<TACOperand>(
|
return std::make_shared<TACOperand>(
|
||||||
|
|
@ -407,7 +446,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// NOUVEAU: HELPERS POUR RÉSOLUTION DE VARIABLES
|
// HELPERS POUR RÉSOLUTION DE VARIABLES
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
std::shared_ptr<Variable> ResolveVariableByUuid(const std::string& uuid) const {
|
std::shared_ptr<Variable> ResolveVariableByUuid(const std::string& uuid) const {
|
||||||
|
|
@ -491,6 +530,15 @@ private:
|
||||||
std::cout << " -> Type: CallFunctionNode\n";
|
std::cout << " -> Type: CallFunctionNode\n";
|
||||||
GenerateCallFunctionNode(node);
|
GenerateCallFunctionNode(node);
|
||||||
}
|
}
|
||||||
|
else if (node->IsType<FunctionEntryNode>()) {
|
||||||
|
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
|
// NŒUDS SYSCALL
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
@ -510,14 +558,31 @@ private:
|
||||||
std::cout << " -> Type: SendSignalNode\n";
|
std::cout << " -> Type: SendSignalNode\n";
|
||||||
GenerateSendSignalNode(node);
|
GenerateSendSignalNode(node);
|
||||||
}
|
}
|
||||||
else if (node->IsType<FunctionEntryNode>()) {
|
|
||||||
std::cout << " -> Type: FunctionEntryNode (generating children)\n";
|
// ===================================================================
|
||||||
// Entry point, générer les enfants
|
// OUR LES BOUCLES
|
||||||
for (size_t i = 0; i < node->children.size(); i++) {
|
// ===================================================================
|
||||||
std::cout << " Processing child [" << i << "]\n";
|
else if (node->IsType<ForLoopNode>()) {
|
||||||
GenerateNode(node->children[i]);
|
std::cout << " -> Type: ForLoopNode\n";
|
||||||
}
|
GenerateForLoopNode(node);
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
else if (node->IsType<WhileLoopNode>()) {
|
||||||
|
std::cout << " -> Type: WhileLoopNode\n";
|
||||||
|
GenerateWhileLoopNode(node);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
else if (node->IsType<BreakNode>()) {
|
||||||
|
std::cout << " -> Type: BreakNode\n";
|
||||||
|
GenerateBreakNode(node);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
else if (node->IsType<ContinueNode>()) {
|
||||||
|
std::cout << " -> Type: ContinueNode\n";
|
||||||
|
GenerateContinueNode(node);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Mémoriser le résultat
|
// Mémoriser le résultat
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|
@ -532,6 +597,291 @@ private:
|
||||||
// GÉNÉRATION PAR TYPE DE NŒUD - EXISTANTS (pas de changement)
|
// GÉNÉRATION PAR TYPE DE NŒUD - EXISTANTS (pas de changement)
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// GÉNÉRATION DU FOR LOOP
|
||||||
|
// ===================================================================
|
||||||
|
void GenerateForLoopNode(std::shared_ptr<ASTNode> node) {
|
||||||
|
auto* forLoopNode = node->GetAs<ForLoopNode>();
|
||||||
|
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<TACOperand> startOperand;
|
||||||
|
auto startInput = node->GetDataInput(1);
|
||||||
|
if (startInput) {
|
||||||
|
startOperand = GenerateNode(startInput);
|
||||||
|
} else {
|
||||||
|
startOperand = std::make_shared<TACOperand>(
|
||||||
|
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>(
|
||||||
|
TACInstruction::OpCode::COPY,
|
||||||
|
indexTemp,
|
||||||
|
startOperand
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(copyInstr);
|
||||||
|
|
||||||
|
// === 2. LABEL loop_start ===
|
||||||
|
auto labelStart = std::make_shared<TACInstruction>(
|
||||||
|
TACInstruction::OpCode::LABEL,
|
||||||
|
loopStart
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(labelStart);
|
||||||
|
|
||||||
|
// === 3. CONDITION : index < end ===
|
||||||
|
std::shared_ptr<TACOperand> endOperand;
|
||||||
|
auto endInput = node->GetDataInput(2);
|
||||||
|
if (endInput) {
|
||||||
|
endOperand = GenerateNode(endInput);
|
||||||
|
} else {
|
||||||
|
endOperand = std::make_shared<TACOperand>(
|
||||||
|
TACOperand::Type::CONSTANT,
|
||||||
|
std::to_string(forLoopNode->GetEndIndex())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test: index < end
|
||||||
|
auto condTemp = NewTemp();
|
||||||
|
auto ltInstr = std::make_shared<TACInstruction>(
|
||||||
|
TACInstruction::OpCode::LT,
|
||||||
|
condTemp,
|
||||||
|
indexTemp,
|
||||||
|
endOperand
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(ltInstr);
|
||||||
|
|
||||||
|
// if (!condition) goto loop_end
|
||||||
|
auto ifFalse = std::make_shared<TACInstruction>(
|
||||||
|
TACInstruction::OpCode::IF_FALSE,
|
||||||
|
loopEnd,
|
||||||
|
condTemp
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(ifFalse);
|
||||||
|
|
||||||
|
// === 4. LABEL loop_body ===
|
||||||
|
auto labelBody = std::make_shared<TACInstruction>(
|
||||||
|
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>(
|
||||||
|
TACInstruction::OpCode::LABEL,
|
||||||
|
loopContinue
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(labelContinue);
|
||||||
|
|
||||||
|
// === 7. INCRÉMENTATION : index = index + step ===
|
||||||
|
std::shared_ptr<TACOperand> stepOperand;
|
||||||
|
auto stepInput = node->GetDataInput(3);
|
||||||
|
if (stepInput) {
|
||||||
|
stepOperand = GenerateNode(stepInput);
|
||||||
|
} else {
|
||||||
|
stepOperand = std::make_shared<TACOperand>(
|
||||||
|
TACOperand::Type::CONSTANT,
|
||||||
|
std::to_string(forLoopNode->GetStep())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto addInstr = std::make_shared<TACInstruction>(
|
||||||
|
TACInstruction::OpCode::ADD,
|
||||||
|
indexTemp,
|
||||||
|
indexTemp,
|
||||||
|
stepOperand
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(addInstr);
|
||||||
|
|
||||||
|
// === 8. RETOUR AU DÉBUT ===
|
||||||
|
auto gotoStart = std::make_shared<TACInstruction>(
|
||||||
|
TACInstruction::OpCode::GOTO,
|
||||||
|
loopStart
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(gotoStart);
|
||||||
|
|
||||||
|
// === 9. LABEL loop_end ===
|
||||||
|
auto labelEnd = std::make_shared<TACInstruction>(
|
||||||
|
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<ASTNode> node) {
|
||||||
|
auto* whileLoopNode = node->GetAs<WhileLoopNode>();
|
||||||
|
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>(
|
||||||
|
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>(
|
||||||
|
TACInstruction::OpCode::IF_FALSE,
|
||||||
|
loopEnd,
|
||||||
|
conditionOperand
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(ifFalse);
|
||||||
|
|
||||||
|
// === 4. LABEL loop_body ===
|
||||||
|
auto labelBody = std::make_shared<TACInstruction>(
|
||||||
|
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>(
|
||||||
|
TACInstruction::OpCode::GOTO,
|
||||||
|
loopStart
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(gotoStart);
|
||||||
|
|
||||||
|
// === 7. LABEL loop_end ===
|
||||||
|
auto labelEnd = std::make_shared<TACInstruction>(
|
||||||
|
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<ASTNode> node) {
|
||||||
|
auto* breakNode = node->GetAs<BreakNode>();
|
||||||
|
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>(
|
||||||
|
TACOperand::Type::LABEL,
|
||||||
|
loopContext.endLabel
|
||||||
|
);
|
||||||
|
|
||||||
|
auto gotoEnd = std::make_shared<TACInstruction>(
|
||||||
|
TACInstruction::OpCode::GOTO,
|
||||||
|
endLabel
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(gotoEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// GÉNÉRATION DU CONTINUE
|
||||||
|
// ===================================================================
|
||||||
|
void GenerateContinueNode(std::shared_ptr<ASTNode> node) {
|
||||||
|
auto* continueNode = node->GetAs<ContinueNode>();
|
||||||
|
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>(
|
||||||
|
TACOperand::Type::LABEL,
|
||||||
|
loopContext.continueLabel
|
||||||
|
);
|
||||||
|
|
||||||
|
auto gotoContinue = std::make_shared<TACInstruction>(
|
||||||
|
TACInstruction::OpCode::GOTO,
|
||||||
|
continueLabel
|
||||||
|
);
|
||||||
|
m_program.AddInstruction(gotoContinue);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<TACOperand> GenerateVariableNode(std::shared_ptr<ASTNode> node) {
|
std::shared_ptr<TACOperand> GenerateVariableNode(std::shared_ptr<ASTNode> node) {
|
||||||
auto* varNode = node->GetAs<VariableNode>();
|
auto* varNode = node->GetAs<VariableNode>();
|
||||||
if (!varNode) {
|
if (!varNode) {
|
||||||
|
|
|
||||||
24
core/story-manager/src/nodes/break_node.cpp
Normal file
24
core/story-manager/src/nodes/break_node.cpp
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
12
core/story-manager/src/nodes/break_node.h
Normal file
12
core/story-manager/src/nodes/break_node.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "base_node.h"
|
||||||
|
|
||||||
|
class BreakNode : public BaseNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BreakNode(const std::string &type = "break-node");
|
||||||
|
|
||||||
|
void Initialize() override;
|
||||||
|
};
|
||||||
24
core/story-manager/src/nodes/continue_node.cpp
Normal file
24
core/story-manager/src/nodes/continue_node.cpp
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
12
core/story-manager/src/nodes/continue_node.h
Normal file
12
core/story-manager/src/nodes/continue_node.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "base_node.h"
|
||||||
|
|
||||||
|
class ContinueNode : public BaseNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ContinueNode(const std::string &type = "continue-node");
|
||||||
|
|
||||||
|
void Initialize() override;
|
||||||
|
};
|
||||||
46
core/story-manager/src/nodes/for_loop_node.cpp
Normal file
46
core/story-manager/src/nodes/for_loop_node.cpp
Normal file
|
|
@ -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<int>();
|
||||||
|
}
|
||||||
|
if (j.contains("end_index")) {
|
||||||
|
m_endIndex = j["end_index"].get<int>();
|
||||||
|
}
|
||||||
|
if (j.contains("step")) {
|
||||||
|
m_step = j["step"].get<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
42
core/story-manager/src/nodes/for_loop_node.h
Normal file
42
core/story-manager/src/nodes/for_loop_node.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#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};
|
||||||
|
};
|
||||||
28
core/story-manager/src/nodes/while_loop_node.cpp
Normal file
28
core/story-manager/src/nodes/while_loop_node.cpp
Normal file
|
|
@ -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();
|
||||||
|
}
|
||||||
12
core/story-manager/src/nodes/while_loop_node.h
Normal file
12
core/story-manager/src/nodes/while_loop_node.h
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include "base_node.h"
|
||||||
|
|
||||||
|
class WhileLoopNode : public BaseNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WhileLoopNode(const std::string &type = "while-loop-node");
|
||||||
|
|
||||||
|
void Initialize() override;
|
||||||
|
};
|
||||||
|
|
@ -20,6 +20,10 @@
|
||||||
#include "wait_event_node.h"
|
#include "wait_event_node.h"
|
||||||
#include "play_media_node.h"
|
#include "play_media_node.h"
|
||||||
#include "send_signal_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 OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec";
|
||||||
static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb";
|
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 WaitDelayNodeUuid = "02455ef0-4975-4546-94de-720cae6baae3";
|
||||||
static const std::string PlayMediaNodeUuid = "0285e90a-2eb7-4605-baa9-b3712a14dff8";
|
static const std::string PlayMediaNodeUuid = "0285e90a-2eb7-4605-baa9-b3712a14dff8";
|
||||||
static const std::string SendSignalNodeUuid = "02c2ce4b-8783-47cb-a55f-90056bebd64b";
|
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<BaseNode> (*GenericCreator)(const std::string &type);
|
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type);
|
||||||
|
|
||||||
|
|
@ -54,6 +62,10 @@ public:
|
||||||
registerNode<WaitDelayNode>(WaitDelayNodeUuid, std::make_shared<StoryPrimitive>("Wait Delay"));
|
registerNode<WaitDelayNode>(WaitDelayNodeUuid, std::make_shared<StoryPrimitive>("Wait Delay"));
|
||||||
registerNode<PlayMediaNode>(PlayMediaNodeUuid, std::make_shared<StoryPrimitive>("Play Media"));
|
registerNode<PlayMediaNode>(PlayMediaNodeUuid, std::make_shared<StoryPrimitive>("Play Media"));
|
||||||
registerNode<SendSignalNode>(SendSignalNodeUuid, std::make_shared<StoryPrimitive>("Send Signal"));
|
registerNode<SendSignalNode>(SendSignalNodeUuid, std::make_shared<StoryPrimitive>("Send Signal"));
|
||||||
|
registerNode<ForLoopNode>(ForLoopNodeUuid, std::make_shared<StoryPrimitive>("For Loop"));
|
||||||
|
registerNode<WhileLoopNode>(WhileLoopNodeUuid, std::make_shared<StoryPrimitive>("While Loop"));
|
||||||
|
registerNode<BreakNode>(BreakNodeUuid, std::make_shared<StoryPrimitive>("Break"));
|
||||||
|
registerNode<ContinueNode>(ContinueNodeUuid, std::make_shared<StoryPrimitive>("Continue"));
|
||||||
}
|
}
|
||||||
|
|
||||||
~NodesFactory() = default;
|
~NodesFactory() = default;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ add_executable(${PROJECT_NAME}
|
||||||
test_ast.cpp
|
test_ast.cpp
|
||||||
test_print_node.cpp
|
test_print_node.cpp
|
||||||
test_branch.cpp
|
test_branch.cpp
|
||||||
|
test_loops.cpp
|
||||||
|
|
||||||
../story-manager/src/nodes/base_node.cpp
|
../story-manager/src/nodes/base_node.cpp
|
||||||
../story-manager/src/nodes/branch_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/wait_delay_node.cpp
|
||||||
../story-manager/src/nodes/play_media_node.cpp
|
../story-manager/src/nodes/play_media_node.cpp
|
||||||
../story-manager/src/nodes/send_signal_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
|
../chip32/chip32_assembler.cpp
|
||||||
|
|
|
||||||
417
core/tests/test_loops.cpp
Normal file
417
core/tests/test_loops.cpp
Normal file
|
|
@ -0,0 +1,417 @@
|
||||||
|
// ===================================================================
|
||||||
|
// core/tests/test_loops.cpp
|
||||||
|
// Tests unitaires pour ForLoop, WhileLoop, Break et Continue
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#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<std::shared_ptr<Variable>> variables;
|
||||||
|
|
||||||
|
// Nodes
|
||||||
|
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
||||||
|
functionEntry->SetWeight(100);
|
||||||
|
|
||||||
|
auto forLoop = std::make_shared<ForLoopNode>("for-loop");
|
||||||
|
forLoop->SetStartIndex(0);
|
||||||
|
forLoop->SetEndIndex(5);
|
||||||
|
forLoop->SetStep(1);
|
||||||
|
forLoop->Initialize();
|
||||||
|
|
||||||
|
auto printNode = std::make_shared<PrintNode>("print-loop");
|
||||||
|
printNode->SetText("Iteration");
|
||||||
|
printNode->Initialize();
|
||||||
|
|
||||||
|
auto printEnd = std::make_shared<PrintNode>("print-end");
|
||||||
|
printEnd->SetText("Loop completed!");
|
||||||
|
printEnd->Initialize();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||||
|
functionEntry,
|
||||||
|
forLoop,
|
||||||
|
printNode,
|
||||||
|
printEnd
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connections
|
||||||
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
|
// Execution: Entry → ForLoop
|
||||||
|
auto execConn1 = std::make_shared<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<std::shared_ptr<Variable>> variables;
|
||||||
|
|
||||||
|
auto varCounter = std::make_shared<Variable>("counter");
|
||||||
|
varCounter->SetIntegerValue(0);
|
||||||
|
variables.push_back(varCounter);
|
||||||
|
|
||||||
|
auto varLimit = std::make_shared<Variable>("limit");
|
||||||
|
varLimit->SetIntegerValue(3);
|
||||||
|
variables.push_back(varLimit);
|
||||||
|
|
||||||
|
// Nodes
|
||||||
|
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
||||||
|
functionEntry->SetWeight(100);
|
||||||
|
|
||||||
|
auto varNodeCounter = std::make_shared<VariableNode>("var-counter");
|
||||||
|
varNodeCounter->SetVariable(varCounter);
|
||||||
|
|
||||||
|
auto varNodeLimit = std::make_shared<VariableNode>("var-limit");
|
||||||
|
varNodeLimit->SetVariable(varLimit);
|
||||||
|
|
||||||
|
// Condition: counter < limit
|
||||||
|
auto compareNode = std::make_shared<OperatorNode>("compare-lt");
|
||||||
|
compareNode->SetOperationType(OperatorNode::OperationType::LESS_THAN);
|
||||||
|
compareNode->Initialize();
|
||||||
|
|
||||||
|
auto whileLoop = std::make_shared<WhileLoopNode>("while-loop");
|
||||||
|
whileLoop->Initialize();
|
||||||
|
|
||||||
|
auto printNode = std::make_shared<PrintNode>("print-loop");
|
||||||
|
printNode->SetText("Counter value");
|
||||||
|
printNode->Initialize();
|
||||||
|
|
||||||
|
auto printEnd = std::make_shared<PrintNode>("print-end");
|
||||||
|
printEnd->SetText("While loop completed!");
|
||||||
|
printEnd->Initialize();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||||
|
functionEntry,
|
||||||
|
varNodeCounter,
|
||||||
|
varNodeLimit,
|
||||||
|
compareNode,
|
||||||
|
whileLoop,
|
||||||
|
printNode,
|
||||||
|
printEnd
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connections
|
||||||
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
|
// Execution: Entry → While
|
||||||
|
auto execConn1 = std::make_shared<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<std::shared_ptr<Variable>> variables;
|
||||||
|
|
||||||
|
// Nodes
|
||||||
|
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
||||||
|
functionEntry->SetWeight(100);
|
||||||
|
|
||||||
|
auto forLoop = std::make_shared<ForLoopNode>("for-loop");
|
||||||
|
forLoop->SetStartIndex(0);
|
||||||
|
forLoop->SetEndIndex(10);
|
||||||
|
forLoop->SetStep(1);
|
||||||
|
forLoop->Initialize();
|
||||||
|
|
||||||
|
auto printNode = std::make_shared<PrintNode>("print-iter");
|
||||||
|
printNode->SetText("Iteration");
|
||||||
|
printNode->Initialize();
|
||||||
|
|
||||||
|
auto breakNode = std::make_shared<BreakNode>("break-node");
|
||||||
|
breakNode->Initialize();
|
||||||
|
|
||||||
|
auto printEnd = std::make_shared<PrintNode>("print-end");
|
||||||
|
printEnd->SetText("Broke out of loop");
|
||||||
|
printEnd->Initialize();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||||
|
functionEntry,
|
||||||
|
forLoop,
|
||||||
|
printNode,
|
||||||
|
breakNode,
|
||||||
|
printEnd
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connections
|
||||||
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
|
// Entry → ForLoop
|
||||||
|
auto execConn1 = std::make_shared<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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<std::shared_ptr<Variable>> variables;
|
||||||
|
|
||||||
|
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
||||||
|
functionEntry->SetWeight(100);
|
||||||
|
|
||||||
|
auto forLoop = std::make_shared<ForLoopNode>("for-loop");
|
||||||
|
forLoop->SetStartIndex(0);
|
||||||
|
forLoop->SetEndIndex(5);
|
||||||
|
forLoop->Initialize();
|
||||||
|
|
||||||
|
auto continueNode = std::make_shared<ContinueNode>("continue-node");
|
||||||
|
continueNode->Initialize();
|
||||||
|
|
||||||
|
auto printNode = std::make_shared<PrintNode>("print-odd");
|
||||||
|
printNode->SetText("Should not print");
|
||||||
|
printNode->Initialize();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||||
|
functionEntry,
|
||||||
|
forLoop,
|
||||||
|
continueNode,
|
||||||
|
printNode
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
|
// Entry → ForLoop
|
||||||
|
auto execConn1 = std::make_shared<Connection>();
|
||||||
|
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<Connection>();
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
@ -152,6 +152,10 @@ set(SRCS
|
||||||
src/node_editor/wait_delay_node_widget.cpp
|
src/node_editor/wait_delay_node_widget.cpp
|
||||||
src/node_editor/play_media_node_widget.cpp
|
src/node_editor/play_media_node_widget.cpp
|
||||||
src/node_editor/send_signal_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/gui.cpp
|
||||||
src/media_converter.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/wait_delay_node.cpp
|
||||||
../core/story-manager/src/nodes/play_media_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/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/nodes/connection.cpp
|
||||||
../core/story-manager/src/story_db.cpp
|
../core/story-manager/src/story_db.cpp
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[Window][WindowOverViewport_11111111]
|
[Window][WindowOverViewport_11111111]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=1220,694
|
Size=1860,982
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Debug##Default]
|
[Window][Debug##Default]
|
||||||
|
|
@ -9,32 +9,32 @@ Size=400,400
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Library Manager]
|
[Window][Library Manager]
|
||||||
Pos=710,26
|
Pos=1060,26
|
||||||
Size=570,469
|
Size=860,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][Console]
|
[Window][Console]
|
||||||
Pos=60,497
|
Pos=60,681
|
||||||
Size=527,223
|
Size=805,327
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,0
|
DockId=0x00000004,0
|
||||||
|
|
||||||
[Window][Emulator]
|
[Window][Emulator]
|
||||||
Pos=710,26
|
Pos=1060,26
|
||||||
Size=570,469
|
Size=860,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,5
|
DockId=0x00000002,5
|
||||||
|
|
||||||
[Window][Code viewer]
|
[Window][Code viewer]
|
||||||
Pos=710,26
|
Pos=1060,26
|
||||||
Size=570,469
|
Size=860,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,4
|
DockId=0x00000002,4
|
||||||
|
|
||||||
[Window][Resources]
|
[Window][Resources]
|
||||||
Pos=710,26
|
Pos=1060,26
|
||||||
Size=570,469
|
Size=860,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000002,1
|
||||||
|
|
||||||
|
|
@ -50,36 +50,36 @@ Size=150,42
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Variables]
|
[Window][Variables]
|
||||||
Pos=589,497
|
Pos=867,681
|
||||||
Size=691,223
|
Size=1053,327
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][CPU]
|
[Window][CPU]
|
||||||
Pos=710,26
|
Pos=1060,26
|
||||||
Size=570,469
|
Size=860,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,2
|
DockId=0x00000002,2
|
||||||
|
|
||||||
[Window][RAM view]
|
[Window][RAM view]
|
||||||
Pos=710,26
|
Pos=1060,26
|
||||||
Size=570,469
|
Size=860,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,3
|
DockId=0x00000002,3
|
||||||
|
|
||||||
[Window][Properties]
|
[Window][Properties]
|
||||||
Pos=589,497
|
Pos=867,681
|
||||||
Size=691,223
|
Size=1053,327
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,1
|
DockId=0x00000005,1
|
||||||
|
|
||||||
[Window][ToolBar]
|
[Window][ToolBar]
|
||||||
Pos=0,26
|
Pos=0,26
|
||||||
Size=60,694
|
Size=60,982
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][QuitConfirm]
|
[Window][QuitConfirm]
|
||||||
Pos=508,312
|
Pos=828,456
|
||||||
Size=264,96
|
Size=264,96
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
|
|
@ -90,13 +90,13 @@ Collapsed=0
|
||||||
|
|
||||||
[Window][Module editor]
|
[Window][Module editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=648,469
|
Size=998,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
[Window][Story editor]
|
[Window][Story editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=648,469
|
Size=998,653
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,1
|
DockId=0x00000001,1
|
||||||
|
|
||||||
|
|
@ -106,8 +106,8 @@ Size=687,422
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Error List]
|
[Window][Error List]
|
||||||
Pos=60,497
|
Pos=60,681
|
||||||
Size=527,223
|
Size=805,327
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,1
|
DockId=0x00000004,1
|
||||||
|
|
||||||
|
|
@ -155,11 +155,11 @@ Column 2 Weight=1.0000
|
||||||
Column 3 Width=60
|
Column 3 Width=60
|
||||||
|
|
||||||
[Docking][Data]
|
[Docking][Data]
|
||||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
|
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1860,982 Split=Y
|
||||||
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,469 Split=X
|
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,365 Split=X
|
||||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=648,694 CentralNode=1 Selected=0x93ADCAAB
|
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=998,694 CentralNode=1 Selected=0x93ADCAAB
|
||||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=570,694 Selected=0x63869CAF
|
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=860,694 Selected=0x4B07C626
|
||||||
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,223 Split=X Selected=0xEA83D666
|
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,327 Split=X Selected=0xEA83D666
|
||||||
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=621,192 Selected=0xEA83D666
|
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=621,192 Selected=0xEA83D666
|
||||||
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=813,192 Selected=0x8C72BEA8
|
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=813,192 Selected=0x8C72BEA8
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,10 @@
|
||||||
#include "send_signal_node_widget.h"
|
#include "send_signal_node_widget.h"
|
||||||
#include "wait_delay_node_widget.h"
|
#include "wait_delay_node_widget.h"
|
||||||
#include "wait_event_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)
|
MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController)
|
||||||
: m_logger(logger)
|
: m_logger(logger)
|
||||||
|
|
@ -72,6 +76,10 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
|
||||||
m_widgetFactory.registerNode<WaitDelayNodeWidget>(WaitDelayNodeUuid);
|
m_widgetFactory.registerNode<WaitDelayNodeWidget>(WaitDelayNodeUuid);
|
||||||
m_widgetFactory.registerNode<PlayMediaNodeWidget>(PlayMediaNodeUuid);
|
m_widgetFactory.registerNode<PlayMediaNodeWidget>(PlayMediaNodeUuid);
|
||||||
m_widgetFactory.registerNode<SendSignalNodeWidget>(SendSignalNodeUuid);
|
m_widgetFactory.registerNode<SendSignalNodeWidget>(SendSignalNodeUuid);
|
||||||
|
m_widgetFactory.registerNode<ForLoopNodeWidget>(ForLoopNodeUuid);
|
||||||
|
m_widgetFactory.registerNode<WhileLoopNodeWidget>(WhileLoopNodeUuid);
|
||||||
|
m_widgetFactory.registerNode<BreakNodeWidget>(BreakNodeUuid);
|
||||||
|
m_widgetFactory.registerNode<ContinueNodeWidget>(ContinueNodeUuid);
|
||||||
|
|
||||||
m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) {
|
m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) {
|
||||||
OpenProject(event.GetUuid());
|
OpenProject(event.GetUuid());
|
||||||
|
|
|
||||||
24
story-editor/src/node_editor/break_node_widget.cpp
Normal file
24
story-editor/src/node_editor/break_node_widget.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "break_node_widget.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "IconsMaterialDesignIcons.h"
|
||||||
|
|
||||||
|
BreakNodeWidget::BreakNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base)
|
||||||
|
: BaseNodeWidget(manager, base)
|
||||||
|
{
|
||||||
|
m_breakNode = std::dynamic_pointer_cast<BreakNode>(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<IStoryProject> 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.");
|
||||||
|
}
|
||||||
17
story-editor/src/node_editor/break_node_widget.h
Normal file
17
story-editor/src/node_editor/break_node_widget.h
Normal file
|
|
@ -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<BaseNode> base);
|
||||||
|
virtual ~BreakNodeWidget() = default;
|
||||||
|
|
||||||
|
void Draw() override;
|
||||||
|
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<BreakNode> m_breakNode;
|
||||||
|
};
|
||||||
24
story-editor/src/node_editor/continue_node_widget.cpp
Normal file
24
story-editor/src/node_editor/continue_node_widget.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#include "continue_node_widget.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "IconsMaterialDesignIcons.h"
|
||||||
|
|
||||||
|
ContinueNodeWidget::ContinueNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> base)
|
||||||
|
: BaseNodeWidget(manager, base)
|
||||||
|
{
|
||||||
|
m_continueNode = std::dynamic_pointer_cast<ContinueNode>(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<IStoryProject> 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.");
|
||||||
|
}
|
||||||
17
story-editor/src/node_editor/continue_node_widget.h
Normal file
17
story-editor/src/node_editor/continue_node_widget.h
Normal file
|
|
@ -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<BaseNode> base);
|
||||||
|
virtual ~ContinueNodeWidget() = default;
|
||||||
|
|
||||||
|
void Draw() override;
|
||||||
|
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<ContinueNode> m_continueNode;
|
||||||
|
};
|
||||||
51
story-editor/src/node_editor/for_loop_node_widget.cpp
Normal file
51
story-editor/src/node_editor/for_loop_node_widget.cpp
Normal file
|
|
@ -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<BaseNode> base)
|
||||||
|
: BaseNodeWidget(manager, base)
|
||||||
|
{
|
||||||
|
m_forLoopNode = std::dynamic_pointer_cast<ForLoopNode>(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<IStoryProject> 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.");
|
||||||
|
}
|
||||||
18
story-editor/src/node_editor/for_loop_node_widget.h
Normal file
18
story-editor/src/node_editor/for_loop_node_widget.h
Normal file
|
|
@ -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<BaseNode> base);
|
||||||
|
virtual ~ForLoopNodeWidget() = default;
|
||||||
|
|
||||||
|
void Draw() override;
|
||||||
|
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<ForLoopNode> m_forLoopNode;
|
||||||
|
};
|
||||||
30
story-editor/src/node_editor/while_loop_node_widget.cpp
Normal file
30
story-editor/src/node_editor/while_loop_node_widget.cpp
Normal file
|
|
@ -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<BaseNode> base)
|
||||||
|
: BaseNodeWidget(manager, base)
|
||||||
|
{
|
||||||
|
m_whileLoopNode = std::dynamic_pointer_cast<WhileLoopNode>(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<IStoryProject> 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.");
|
||||||
|
}
|
||||||
18
story-editor/src/node_editor/while_loop_node_widget.h
Normal file
18
story-editor/src/node_editor/while_loop_node_widget.h
Normal file
|
|
@ -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<BaseNode> base);
|
||||||
|
virtual ~WhileLoopNodeWidget() = default;
|
||||||
|
|
||||||
|
void Draw() override;
|
||||||
|
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<WhileLoopNode> m_whileLoopNode;
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue