From c29d099ec933d2ab49b65281c6fc580cd8e2d7f5 Mon Sep 17 00:00:00 2001 From: Anthony Rabine Date: Mon, 20 Oct 2025 19:44:53 +0200 Subject: [PATCH] Modified TAC for all new syscall widgets --- .../src/compiler/assembly_generator.h | 4 + .../compiler/assembly_generator_chip32_tac.h | 2 +- core/story-manager/src/compiler/tac.h | 428 ++++++++++++++---- core/story-manager/src/story_project.cpp | 2 +- core/tests/test_ast.cpp | 17 +- 5 files changed, 359 insertions(+), 94 deletions(-) diff --git a/core/story-manager/src/compiler/assembly_generator.h b/core/story-manager/src/compiler/assembly_generator.h index 407e230..be15e45 100644 --- a/core/story-manager/src/compiler/assembly_generator.h +++ b/core/story-manager/src/compiler/assembly_generator.h @@ -130,6 +130,10 @@ public: return m_assembly.str(); } + GeneratorContext& Context() { + return m_context; + } + protected: virtual void GenerateNodeCode(std::shared_ptr node, bool isDataPath = false) = 0; diff --git a/core/story-manager/src/compiler/assembly_generator_chip32_tac.h b/core/story-manager/src/compiler/assembly_generator_chip32_tac.h index 4153c05..797fd5e 100644 --- a/core/story-manager/src/compiler/assembly_generator_chip32_tac.h +++ b/core/story-manager/src/compiler/assembly_generator_chip32_tac.h @@ -49,7 +49,7 @@ public: // === ÉTAPE 3 : GÉNÉRATION TAC === m_tacGenerator = std::make_unique(); - m_tacProgram = m_tacGenerator->Generate(astNodes); + m_tacProgram = m_tacGenerator->Generate(astNodes, Context().variables); // DEBUG : Afficher le TAC généré if (m_context.debugOutput) { diff --git a/core/story-manager/src/compiler/tac.h b/core/story-manager/src/compiler/tac.h index e4b3ae0..5e87f5c 100644 --- a/core/story-manager/src/compiler/tac.h +++ b/core/story-manager/src/compiler/tac.h @@ -23,6 +23,10 @@ class PrintNode; class BranchNode; class FunctionEntryNode; class CallFunctionNode; +class WaitEventNode; +class WaitDelayNode; +class PlayMediaNode; +class SendSignalNode; // =================================================================== // TAC Operand - Représente une opérande (variable, temporaire, constante) @@ -305,6 +309,10 @@ private: // Note: Nécessite les includes des types de nœuds concrets // =================================================================== +// =================================================================== +// SECTION 1: INCLUDES (à ajouter après les includes existants) +// =================================================================== + #include "ast_builder.h" #include "variable_node.h" #include "operator_node.h" @@ -312,15 +320,30 @@ private: #include "branch_node.h" #include "function_entry_node.h" #include "call_function_node.h" +#include "wait_event_node.h" +#include "wait_delay_node.h" +#include "play_media_node.h" +#include "send_signal_node.h" + +// =================================================================== +// SECTION 2: CLASSE TACGenerator - MODIFICATIONS +// =================================================================== class TACGenerator { public: TACGenerator() : m_tempCounter(0), m_labelCounter(0) {} - // Génère le TAC à partir de l'AST - TACProgram Generate(const std::vector>& astNodes) { + // =================================================================== + // NOUVELLE SIGNATURE: Génère le TAC à partir de l'AST + Variables + // =================================================================== + TACProgram Generate(const std::vector>& astNodes, + const std::vector>& variables) { std::cout << "\n=== TACGenerator::Generate() START ===\n"; std::cout << "Number of AST nodes received: " << astNodes.size() << "\n"; + std::cout << "Number of variables: " << variables.size() << "\n"; + + // Stocker les variables pour résolution ultérieure + m_variables = variables; m_program.Clear(); m_tempCounter = 0; @@ -328,8 +351,7 @@ public: m_nodeResults.clear(); m_visitedNodes.clear(); - // Simplement appeler GenerateNode pour chaque nœud - // La gestion des doublons se fait dans GenerateNode() + // Générer le code pour chaque nœud for (size_t i = 0; i < astNodes.size(); i++) { const auto& node = astNodes[i]; std::cout << "Processing AST node [" << i << "]: " @@ -360,6 +382,9 @@ private: // Set pour éviter de visiter deux fois le même nœud std::set m_visitedNodes; + + // NOUVEAU: Variables disponibles pour résolution + std::vector> m_variables; // =================================================================== // HELPERS @@ -382,17 +407,43 @@ private: } // =================================================================== - // GÉNÉRATION RÉCURSIVE + // NOUVEAU: HELPERS POUR RÉSOLUTION DE VARIABLES + // =================================================================== + + std::shared_ptr ResolveVariableByUuid(const std::string& uuid) const { + if (uuid.empty()) { + return nullptr; + } + + for (const auto& var : m_variables) { + if (var->GetUuid() == uuid) { + return var; + } + } + + std::cerr << "WARNING: Variable with UUID " << uuid << " not found!\n"; + return nullptr; + } + + std::string GetVariableLabel(const std::string& uuid) const { + auto var = ResolveVariableByUuid(uuid); + if (var) { + return var->GetLabel(); + } + return "unknown_var"; + } + + // =================================================================== + // GÉNÉRATION RÉCURSIVE - DISPATCHER // =================================================================== - // Génère le code pour un nœud et retourne où est stocké son résultat std::shared_ptr GenerateNode(std::shared_ptr node) { if (!node) return nullptr; std::cout << " GenerateNode(" << node->node->GetTypeName() << ", ID: " << node->GetId() << ")\n"; - // NOUVEAU : Vérifier si ce nœud a déjà été complètement traité + // Vérifier si ce nœud a déjà été complètement traité if (m_visitedNodes.find(node->GetId()) != m_visitedNodes.end()) { std::cout << " -> Node already fully processed, SKIPPING\n"; // Retourner le résultat en cache s'il existe @@ -416,6 +467,10 @@ private: std::shared_ptr result = nullptr; + // =================================================================== + // DISPATCHER PAR TYPE DE NŒUD + // =================================================================== + if (node->IsType()) { std::cout << " -> Type: VariableNode\n"; result = GenerateVariableNode(node); @@ -436,6 +491,25 @@ private: std::cout << " -> Type: CallFunctionNode\n"; GenerateCallFunctionNode(node); } + // =================================================================== + // NŒUDS SYSCALL + // =================================================================== + else if (node->IsType()) { + std::cout << " -> Type: WaitEventNode\n"; + GenerateWaitEventNode(node); + } + else if (node->IsType()) { + std::cout << " -> Type: WaitDelayNode\n"; + GenerateWaitDelayNode(node); + } + else if (node->IsType()) { + std::cout << " -> Type: PlayMediaNode\n"; + GeneratePlayMediaNode(node); + } + else if (node->IsType()) { + std::cout << " -> Type: SendSignalNode\n"; + GenerateSendSignalNode(node); + } else if (node->IsType()) { std::cout << " -> Type: FunctionEntryNode (generating children)\n"; // Entry point, générer les enfants @@ -455,7 +529,7 @@ private: } // =================================================================== - // GÉNÉRATION PAR TYPE DE NŒUD + // GÉNÉRATION PAR TYPE DE NŒUD - EXISTANTS (pas de changement) // =================================================================== std::shared_ptr GenerateVariableNode(std::shared_ptr node) { @@ -564,94 +638,27 @@ private: // RÉCUPÉRER ET CONVERTIR LE FORMAT STRING std::string formatString = printNode->GetText(); - // Évaluer tous les arguments et déterminer leurs types + // Évaluer tous les arguments std::vector> args; - std::vector argTypes; // Collecter et trier les inputs par port index std::vector>> sortedInputs; for (const auto& [port, inputNode] : node->dataInputs) { - std::cout << " Found input at port " << port - << " -> " << inputNode->node->GetTypeName() << "\n"; sortedInputs.push_back({port, inputNode}); } std::sort(sortedInputs.begin(), sortedInputs.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); - // Générer le code pour chaque argument ET récupérer son type + // Générer le code pour chaque argument for (const auto& [port, inputNode] : sortedInputs) { - std::cout << " Processing input port " << port << "\n"; auto argOperand = GenerateNode(inputNode); if (argOperand) { - std::cout << " -> Got operand: " << argOperand->ToString() << "\n"; args.push_back(argOperand); - - // DÉTERMINER LE TYPE DE L'ARGUMENT - Variable::ValueType argType = Variable::ValueType::INTEGER; // défaut - - if (inputNode->IsType()) { - auto* varNode = inputNode->GetAs(); - auto var = varNode->GetVariable(); - if (var) { - argType = var->GetValueType(); - std::cout << " -> Variable type: " - << Variable::ValueTypeToString(argType) << "\n"; - } - } - // Pour les OperatorNode, le résultat est toujours un INTEGER - else if (inputNode->IsType()) { - argType = Variable::ValueType::INTEGER; - } - - argTypes.push_back(argType); } } - std::cout << " Total args collected: " << args.size() << "\n"; - - // CONVERTIR LES PLACEHOLDERS EN FONCTION DU TYPE - for (size_t i = 0; i < argTypes.size() && i < 4; i++) { - std::string placeholder = "{" + std::to_string(i) + "}"; - std::string formatSpec; - - switch (argTypes[i]) { - case Variable::ValueType::STRING: - formatSpec = "{" + std::to_string(i) + ":s}"; - break; - case Variable::ValueType::INTEGER: - formatSpec = "{" + std::to_string(i) + ":d}"; - break; - case Variable::ValueType::FLOAT: - formatSpec = "{" + std::to_string(i) + ":f}"; - break; - default: - formatSpec = "{" + std::to_string(i) + ":d}"; - } - - // Remplacer {0} par {0:d} ou {0:s} - size_t pos = formatString.find(placeholder); - if (pos != std::string::npos) { - formatString.replace(pos, placeholder.length(), formatSpec); - } - } - - // METTRE À JOUR LE FORMAT STRING DANS LA VARIABLE - auto formatVar = printNode->GetVariable(printNode->GetLabel()); - if (formatVar) { - formatVar->SetTextValue(formatString); - std::cout << " Updated format string to: " << formatString << "\n"; - } - - // Créer l'opérande pour la chaîne de format (avec le format mis à jour) - auto formatOperand = std::make_shared( - TACOperand::Type::VARIABLE, - printNode->GetLabel() - ); - // Générer les instructions PARAM pour chaque argument for (size_t i = 0; i < args.size(); i++) { - std::cout << " Generating PARAM instruction [" << i << "] for " - << args[i]->ToString() << "\n"; auto paramInstr = std::make_shared( TACInstruction::OpCode::PARAM, args[i] @@ -659,8 +666,13 @@ private: m_program.AddInstruction(paramInstr); } + // Créer l'opérande pour la chaîne de format + auto formatOperand = std::make_shared( + TACOperand::Type::VARIABLE, + printNode->GetLabel() + ); + // Générer l'instruction PRINT - std::cout << " Generating PRINT instruction\n"; auto printInstr = std::make_shared( TACInstruction::OpCode::PRINT, formatOperand @@ -702,7 +714,6 @@ private: } // Évaluer la condition (le BranchNode reçoit la condition sur le port 1) - // La condition provient généralement d'un OperatorNode (comparaison) auto conditionInput = node->GetDataInput(1); if (!conditionInput) { throw std::runtime_error("BranchNode missing condition input on port 1"); @@ -765,11 +776,262 @@ private: ); m_program.AddInstruction(labelEnd); } + + // =================================================================== + // NOUVEAUX GÉNÉRATEURS POUR LES NŒUDS SYSCALL + // =================================================================== + + void GenerateWaitDelayNode(std::shared_ptr node) { + auto* waitDelayNode = node->GetAs(); + if (!waitDelayNode) return; + + std::cout << " GenerateWaitDelayNode: duration=" + << waitDelayNode->GetDuration() << "ms\n"; + + // Vérifier s'il y a un override depuis un port data (IN 1) + auto durationInput = node->GetDataInput(1); + std::shared_ptr durationOperand; + + if (durationInput) { + // Évaluer l'expression connectée + durationOperand = GenerateNode(durationInput); + std::cout << " Duration from port 1: " << durationOperand->ToString() << "\n"; + } else { + // Utiliser la valeur par défaut + durationOperand = std::make_shared( + TACOperand::Type::CONSTANT, + std::to_string(waitDelayNode->GetDuration()) + ); + std::cout << " Duration from property: " << waitDelayNode->GetDuration() << "\n"; + } + + // PARAM pour la durée (R0) + auto paramInstr = std::make_shared( + TACInstruction::OpCode::PARAM, + durationOperand + ); + m_program.AddInstruction(paramInstr); + + // SYSCALL 5 + auto syscallNumOperand = std::make_shared( + TACOperand::Type::CONSTANT, "5" + ); + auto syscallInstr = std::make_shared( + TACInstruction::OpCode::SYSCALL, + syscallNumOperand + ); + m_program.AddInstruction(syscallInstr); + } + + void GenerateSendSignalNode(std::shared_ptr node) { + auto* sendSignalNode = node->GetAs(); + if (!sendSignalNode) return; + + std::cout << " GenerateSendSignalNode: signal_id=" + << sendSignalNode->GetSignalId() << "\n"; + + // Vérifier s'il y a un override depuis un port data (IN 1) + auto signalInput = node->GetDataInput(1); + std::shared_ptr signalOperand; + + if (signalInput) { + signalOperand = GenerateNode(signalInput); + std::cout << " Signal ID from port 1: " << signalOperand->ToString() << "\n"; + } else { + signalOperand = std::make_shared( + TACOperand::Type::CONSTANT, + std::to_string(sendSignalNode->GetSignalId()) + ); + std::cout << " Signal ID from property: " << sendSignalNode->GetSignalId() << "\n"; + } + + // PARAM pour le signal ID (R0) + auto paramInstr = std::make_shared( + TACInstruction::OpCode::PARAM, + signalOperand + ); + m_program.AddInstruction(paramInstr); + + // SYSCALL 3 + auto syscallNumOperand = std::make_shared( + TACOperand::Type::CONSTANT, "3" + ); + auto syscallInstr = std::make_shared( + TACInstruction::OpCode::SYSCALL, + syscallNumOperand + ); + m_program.AddInstruction(syscallInstr); + } + + void GeneratePlayMediaNode(std::shared_ptr node) { + auto* playMediaNode = node->GetAs(); + if (!playMediaNode) return; + + std::cout << " GeneratePlayMediaNode (data-driven)\n"; + + // Image name (R0) - depuis le port data IN 1 + auto imageInput = node->GetDataInput(1); + std::shared_ptr imageOperand; + + if (imageInput) { + imageOperand = GenerateNode(imageInput); + std::cout << " Image from port 1: " << imageOperand->ToString() << "\n"; + } else { + // Pas de connexion = null pointer (0) + imageOperand = std::make_shared( + TACOperand::Type::CONSTANT, "0" + ); + std::cout << " No image connected (null)\n"; + } + + // Sound name (R1) - depuis le port data IN 2 + auto soundInput = node->GetDataInput(2); + std::shared_ptr soundOperand; + + if (soundInput) { + soundOperand = GenerateNode(soundInput); + std::cout << " Sound from port 2: " << soundOperand->ToString() << "\n"; + } else { + // Pas de connexion = null pointer (0) + soundOperand = std::make_shared( + TACOperand::Type::CONSTANT, "0" + ); + std::cout << " No sound connected (null)\n"; + } + + // PARAMs + auto paramImage = std::make_shared( + TACInstruction::OpCode::PARAM, imageOperand + ); + m_program.AddInstruction(paramImage); + + auto paramSound = std::make_shared( + TACInstruction::OpCode::PARAM, soundOperand + ); + m_program.AddInstruction(paramSound); + + // SYSCALL 1 + auto syscallNumOperand = std::make_shared( + TACOperand::Type::CONSTANT, "1" + ); + auto syscallInstr = std::make_shared( + TACInstruction::OpCode::SYSCALL, + syscallNumOperand + ); + m_program.AddInstruction(syscallInstr); + + // Optionnel: stocker le status de retour + std::string statusVarUuid = playMediaNode->GetStatusVariableUuid(); + if (!statusVarUuid.empty()) { + std::string varLabel = GetVariableLabel(statusVarUuid); + std::cout << " Storing status in variable: " << varLabel << "\n"; + + auto resultOperand = std::make_shared( + TACOperand::Type::VARIABLE, + varLabel + ); + + auto r0Operand = std::make_shared( + TACOperand::Type::REGISTER, "r0" + ); + + auto storeInstr = std::make_shared( + TACInstruction::OpCode::STORE, + resultOperand, + r0Operand + ); + m_program.AddInstruction(storeInstr); + } + } + + void GenerateWaitEventNode(std::shared_ptr node) { + auto* waitEventNode = node->GetAs(); + if (!waitEventNode) return; + + std::cout << " GenerateWaitEventNode: mask=0x" + << std::hex << waitEventNode->GetEventMask() << std::dec + << ", timeout=" << waitEventNode->GetTimeout() << "ms\n"; + + // Event mask (R0) + auto maskInput = node->GetDataInput(1); + std::shared_ptr maskOperand; + + if (maskInput) { + maskOperand = GenerateNode(maskInput); + std::cout << " Event mask from port 1: " << maskOperand->ToString() << "\n"; + } else { + maskOperand = std::make_shared( + TACOperand::Type::CONSTANT, + std::to_string(waitEventNode->GetEventMask()) + ); + std::cout << " Event mask from property: 0x" << std::hex + << waitEventNode->GetEventMask() << std::dec << "\n"; + } + + // Timeout (R1) + auto timeoutInput = node->GetDataInput(2); + std::shared_ptr timeoutOperand; + + if (timeoutInput) { + timeoutOperand = GenerateNode(timeoutInput); + std::cout << " Timeout from port 2: " << timeoutOperand->ToString() << "\n"; + } else { + timeoutOperand = std::make_shared( + TACOperand::Type::CONSTANT, + std::to_string(waitEventNode->GetTimeout()) + ); + std::cout << " Timeout from property: " << waitEventNode->GetTimeout() << "ms\n"; + } + + // PARAMs + auto paramMask = std::make_shared( + TACInstruction::OpCode::PARAM, maskOperand + ); + m_program.AddInstruction(paramMask); + + auto paramTimeout = std::make_shared( + TACInstruction::OpCode::PARAM, timeoutOperand + ); + m_program.AddInstruction(paramTimeout); + + // SYSCALL 2 + auto syscallNumOperand = std::make_shared( + TACOperand::Type::CONSTANT, "2" + ); + auto syscallInstr = std::make_shared( + TACInstruction::OpCode::SYSCALL, + syscallNumOperand + ); + m_program.AddInstruction(syscallInstr); + + // IMPORTANT: Stocker le résultat dans la variable configurée + std::string resultVarUuid = waitEventNode->GetResultVariableUuid(); + if (!resultVarUuid.empty()) { + std::string varLabel = GetVariableLabel(resultVarUuid); + std::cout << " Storing result in variable: " << varLabel << "\n"; + + auto resultOperand = std::make_shared( + TACOperand::Type::VARIABLE, + varLabel + ); + + auto r0Operand = std::make_shared( + TACOperand::Type::REGISTER, "r0" + ); + + auto storeInstr = std::make_shared( + TACInstruction::OpCode::STORE, + resultOperand, + r0Operand + ); + m_program.AddInstruction(storeInstr); + } else { + std::cout << " No result variable, R0 will contain event code\n"; + } + } + }; // =================================================================== -// FIN DU FICHIER +// FIN DES ADDITIONS // =================================================================== -// Note: La conversion TAC → Assembleur est dans AssemblyGeneratorChip32TAC -// Ce fichier ne contient QUE la représentation TAC pure -// =================================================================== \ No newline at end of file diff --git a/core/story-manager/src/story_project.cpp b/core/story-manager/src/story_project.cpp index 6011958..5fff668 100644 --- a/core/story-manager/src/story_project.cpp +++ b/core/story-manager/src/story_project.cpp @@ -422,7 +422,7 @@ bool StoryProject::GenerateCompleteProgram(std::string &assembly) // Générer le TAC pour cette page TACGenerator tacGen; - TACProgram pageTAC = tacGen.Generate(astNodes); + TACProgram pageTAC = tacGen.Generate(astNodes, m_variables); // Stocker le TAC pageTACPrograms[pageUuid] = pageTAC; diff --git a/core/tests/test_ast.cpp b/core/tests/test_ast.cpp index f1c6dc4..7d33262 100644 --- a/core/tests/test_ast.cpp +++ b/core/tests/test_ast.cpp @@ -256,12 +256,15 @@ TEST_CASE( "Check AST with basic nodes" ) { TEST_CASE("TAC Generation - Print with 2 variables", "[tac][print]") { // === SETUP === + std::vector> variables; // Créer les variables auto var_a = std::make_shared("a"); var_a->SetIntegerValue(10); + variables.push_back(var_a); auto var_b = std::make_shared("b"); var_b->SetIntegerValue(20); + variables.push_back(var_b); // Créer les nœuds auto functionEntry = std::make_shared("function-entry-node"); @@ -320,7 +323,8 @@ TEST_CASE("TAC Generation - Print with 2 variables", "[tac][print]") { // === GENERATE TAC === TACGenerator tacGen; - TACProgram tac = tacGen.Generate(astNodes); + + TACProgram tac = tacGen.Generate(astNodes, variables); // === DISPLAY TAC (for debugging) === std::cout << "\n" << tac.ToString() << std::endl; @@ -688,7 +692,7 @@ TEST_CASE("Complex AST with TAC - Intermediate results and reuse", "[tac][comple // === GENERATE TAC === std::cout << "\n--- Generating TAC ---\n"; TACGenerator tacGen; - TACProgram tac = tacGen.Generate(pathTree); + TACProgram tac = tacGen.Generate(pathTree, variables); std::cout << "\n" << tac.ToString() << std::endl; @@ -760,11 +764,6 @@ TEST_CASE("Complex AST with TAC - Intermediate results and reuse", "[tac][comple REQUIRE(assembly.find(".main:") != std::string::npos); REQUIRE(assembly.find("halt") != std::string::npos); - // Vérifier la conversion {0} → %d dans les format strings - REQUIRE(assembly.find("{0}") == std::string::npos); - REQUIRE(assembly.find("{1}") == std::string::npos); - REQUIRE(assembly.find("{2}") == std::string::npos); - // Vérifier présence de syscall 4 (print) REQUIRE(assembly.find("syscall 4") != std::string::npos); @@ -1094,7 +1093,7 @@ TestResult RunComplexTACTest(int valueA, int valueB, int valueC, int threshold) // Generate TAC TACGenerator tacGen; - TACProgram tac = tacGen.Generate(pathTree); + TACProgram tac = tacGen.Generate(pathTree, variables); result.tacOutput = tac.ToString(); // Verify TAC structure @@ -1150,7 +1149,7 @@ TEST_CASE("Complex AST with TAC - Multiple test cases", "[tac][complex][ast2]") auto [nodes, conns] = BuildComplexDiagram(vars, 10, 5, 3, 40); ASTBuilder builder(nodes, conns); auto tree = builder.BuildAST(); - TACProgram tac = tacGen.Generate(tree); + TACProgram tac = tacGen.Generate(tree, vars); int addCount = 0, subCount = 0, mulCount = 0, gtCount = 0; for (const auto& instr : tac.GetInstructions()) {