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 "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<LoopContext> m_loopStack;
|
||||
|
||||
// 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;
|
||||
|
|
@ -383,13 +397,38 @@ private:
|
|||
// Set pour éviter de visiter deux fois le même nœud
|
||||
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;
|
||||
|
||||
// ===================================================================
|
||||
// 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
|
||||
std::shared_ptr<TACOperand> NewTemp() {
|
||||
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 {
|
||||
|
|
@ -491,6 +530,15 @@ private:
|
|||
std::cout << " -> Type: CallFunctionNode\n";
|
||||
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
|
||||
// ===================================================================
|
||||
|
|
@ -510,14 +558,31 @@ private:
|
|||
std::cout << " -> Type: SendSignalNode\n";
|
||||
GenerateSendSignalNode(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]);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// OUR LES BOUCLES
|
||||
// ===================================================================
|
||||
else if (node->IsType<ForLoopNode>()) {
|
||||
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
|
||||
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<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) {
|
||||
auto* varNode = node->GetAs<VariableNode>();
|
||||
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 "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<BaseNode> (*GenericCreator)(const std::string &type);
|
||||
|
||||
|
|
@ -54,6 +62,10 @@ public:
|
|||
registerNode<WaitDelayNode>(WaitDelayNodeUuid, std::make_shared<StoryPrimitive>("Wait Delay"));
|
||||
registerNode<PlayMediaNode>(PlayMediaNodeUuid, std::make_shared<StoryPrimitive>("Play Media"));
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
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/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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WaitDelayNodeWidget>(WaitDelayNodeUuid);
|
||||
m_widgetFactory.registerNode<PlayMediaNodeWidget>(PlayMediaNodeUuid);
|
||||
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) {
|
||||
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