Add branch node, unit test and fixes
Some checks are pending
Build-StoryEditor / build_linux (push) Waiting to run
Build-StoryEditor / build_win32 (push) Waiting to run

This commit is contained in:
Anthony Rabine 2025-10-17 15:24:47 +02:00
parent 41914362d5
commit 9db4bae9fd
13 changed files with 878 additions and 63 deletions

View file

@ -85,9 +85,9 @@ typedef enum
OP_SKIPNZ = 27, ///< skip next instruction if not zero, e.g.: skipnz r2
// Comparison
OP_CMP_EQ = 28, ///< compare two registers for equality, result in first e.g.: eq r4, r0, r1 (r4 = (r0 == r1 ? 1 : 0)
OP_CMP_GT = 29, ///< compare if first register is greater than the second, result in first e.g.: gt r4, r0, r1
OP_CMP_LT = 30, ///< compare if first register is less than the second, result in first e.g.: lt r4, r0, r1
OP_CMP_EQ = 28, ///< compare two registers for equality, result in first e.g.: eq r4, r0, r1 ; similar to: r4 = (r0 == r1 ? 1 : 0
OP_CMP_GT = 29, ///< compare if first register is greater than the second, result in first e.g.: gt r4, r0, r1 ; similar to: r4 = (r0 > r1 ? 1 : 0
OP_CMP_LT = 30, ///< compare if first register is less than the second, result in first e.g.: lt r4, r0, r1 ; similar to: r4 = (r0 < r1 ? 1 : 0
INSTRUCTION_COUNT
} chip32_instruction_t;

View file

@ -462,13 +462,13 @@ private:
void GenerateIfFalse(std::shared_ptr<TACInstruction> instr) {
LoadOperand(instr->GetOp1(), "r0");
m_assembly << " skipz r0\n";
m_assembly << " skipnz r0\n";
m_assembly << " jump " << instr->GetDest()->ToString() << "\n";
}
void GenerateIfTrue(std::shared_ptr<TACInstruction> instr) {
LoadOperand(instr->GetOp1(), "r0");
m_assembly << " skipnz r0\n";
m_assembly << " skipz r0\n";
m_assembly << " jump " << instr->GetDest()->ToString() << "\n";
}
@ -601,9 +601,9 @@ private:
break;
case Variable::ValueType::INTEGER:
// CORRECTION : Déclarer 1 élément, pas la valeur !
m_assembly << "$" << v->GetLabel() << " DV32, 1 ; "
<< v->GetVariableName() << "\n";
m_assembly << "$" << v->GetLabel() << " DV32, "
<< v->GetValue<int>() << " ; "
<< v->GetVariableName() << "\n";
break;
case Variable::ValueType::FLOAT:

View file

@ -696,7 +696,13 @@ private:
}
void GenerateBranchNode(std::shared_ptr<ASTNode> node) {
auto* branchNode = node->GetAs<BranchNode>();
if (!branchNode) {
throw std::runtime_error("BranchNode cast failed");
}
// Évaluer la condition (le BranchNode reçoit la condition sur le port 1)
// La condition provient généralement d'un OperatorNode (comparaison)
auto conditionInput = node->GetDataInput(1);
if (!conditionInput) {
throw std::runtime_error("BranchNode missing condition input on port 1");
@ -707,11 +713,12 @@ private:
throw std::runtime_error("BranchNode condition evaluation returned null");
}
// Créer les labels pour true/false
auto trueLabel = NewLabel("true_branch");
auto falseLabel = NewLabel("false_branch");
auto endLabel = NewLabel("end_branch");
// Créer les labels pour true/false/end
auto trueLabel = NewLabel("branch_true");
auto falseLabel = NewLabel("branch_false");
auto endLabel = NewLabel("branch_end");
// Convention: 0 = false, toute autre valeur = true
// if (!condition) goto falseLabel
auto ifInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::IF_FALSE,
@ -721,11 +728,11 @@ private:
m_program.AddInstruction(ifInstr);
// True branch label
auto labelInstr1 = std::make_shared<TACInstruction>(
auto labelTrue = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
trueLabel
);
m_program.AddInstruction(labelInstr1);
m_program.AddInstruction(labelTrue);
// True branch code
if (node->GetChild(0)) {
@ -740,11 +747,11 @@ private:
m_program.AddInstruction(gotoEnd);
// False branch label
auto labelInstr2 = std::make_shared<TACInstruction>(
auto labelFalse = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
falseLabel
);
m_program.AddInstruction(labelInstr2);
m_program.AddInstruction(labelFalse);
// False branch code
if (node->GetChild(1)) {
@ -752,11 +759,11 @@ private:
}
// End label
auto labelInstr3 = std::make_shared<TACInstruction>(
auto labelEnd = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
endLabel
);
m_program.AddInstruction(labelInstr3);
m_program.AddInstruction(labelEnd);
}
};

View file

@ -3,16 +3,38 @@
#include "connection.h"
#include "sys_lib.h"
BranchNode::BranchNode(const std::string &type)
: BaseNode(type, "Branch Node")
{
// Configuration du nœud de branchement
SetBehavior(BaseNode::BEHAVIOR_EXECUTION);
// Port d'entrée 0: Exécution (EXECUTION_PORT)
// Port d'entrée 1: Condition (DATA_PORT)
AddInputPort(Port::EXECUTION_PORT, ">", true);
AddInputPort(Port::DATA_PORT, "condition");
// Port de sortie 0: Branche TRUE (EXECUTION_PORT)
// Port de sortie 1: Branche FALSE (EXECUTION_PORT)
AddOutputPort(Port::EXECUTION_PORT, "true", true);
AddOutputPort(Port::EXECUTION_PORT, "false", true);
// Initialiser les données internes
nlohmann::json j;
j["node_type"] = "branch";
j["version"] = 1;
SetInternalData(j);
}
void BranchNode::Initialize()
{
}
// Charger les données sauvegardées
nlohmann::json j = GetInternalData();
// Vérifier la version pour compatibilité future
if (j.contains("version"))
{
int version = j["version"].get<int>();
// Gérer les migrations de version si nécessaire
}
}

View file

@ -6,13 +6,32 @@
#include "i_script_node.h"
#include "i_story_project.h"
/**
* @brief Nœud de branchement conditionnel simple
*
* Ce nœud évalue une condition booléenne et exécute l'une des deux branches.
* La condition provient d'un OperatorNode ou de toute autre source de données.
*
* Convention booléenne:
* - 0 = false
* - toute autre valeur = true
*
* Ports:
* - Entrée 0: Flux d'exécution (EXECUTION_PORT)
* - Entrée 1: Condition (DATA_PORT) - résultat d'un OperatorNode par exemple
* - Sortie 0: Branche TRUE (EXECUTION_PORT)
* - Sortie 1: Branche FALSE (EXECUTION_PORT)
*
* Utilisation typique:
* [Variable A]
* > [OperatorNode >] > [BranchNode]
* [Variable B]
* TRUE FALSE
*/
class BranchNode : public BaseNode
{
public:
BranchNode(const std::string &type);
virtual void Initialize() override;
private:
};
};

View file

@ -16,7 +16,7 @@
#include "story_project.h"
#include "story_primitive.h"
#include "function_entry_node.h"
#include "branch_node.h"
static const std::string OperatorNodeUuid = "0226fdac-8f7a-47d7-8584-b23aceb712ec";
static const std::string CallFunctionNodeUuid = "02745f38-9b11-49fe-94b1-b2a6b78249fb";
@ -24,7 +24,7 @@ static const std::string VariableNodeUuid = "020cca4e-9cdc-47e7-a6a5-53e4c9152ed
static const std::string PrintNodeUuid = "02ee27bc-ff1d-4f94-b700-eab55052ad1c";
static const std::string SyscallNodeUuid = "02225cff-4975-400e-8130-41524d8af773";
static const std::string FunctionEntryNodeUuid = "02fd145a-b3a6-43c2-83ce-6a187e6d4b5b";
static const std::string DUMMY_a_prendre_Uuid = "027b723d-2327-4646-a17a-79ddc2e016e4";
static const std::string BranchNodeUuid = "027b723d-2327-4646-a17a-79ddc2e016e4";
typedef std::shared_ptr<BaseNode> (*GenericCreator)(const std::string &type);
@ -44,6 +44,8 @@ public:
registerNode<PrintNode>(PrintNodeUuid, std::make_shared<StoryPrimitive>("Print"));
registerNode<SyscallNode>(SyscallNodeUuid, std::make_shared<StoryPrimitive>("System call"));
registerNode<FunctionEntryNode>(FunctionEntryNodeUuid, std::make_shared<StoryPrimitive>("Function entry"));
registerNode<BranchNode>(BranchNodeUuid, std::make_shared<StoryPrimitive>("Branch"));
}
~NodesFactory() = default;

View file

@ -30,6 +30,7 @@ add_executable(${PROJECT_NAME}
test_vm.cpp
test_ast.cpp
test_print_node.cpp
test_branch.cpp
../story-manager/src/nodes/base_node.cpp
../story-manager/src/nodes/branch_node.cpp

672
core/tests/test_branch.cpp Normal file
View file

@ -0,0 +1,672 @@
// ===================================================================
// test_branch_node.cpp
// Tests complets pour BranchNode avec différents types de conditions
// ===================================================================
#include <catch2/catch_test_macros.hpp>
#include "branch_node.h"
#include "variable_node.h"
#include "function_entry_node.h"
#include "operator_node.h"
#include "print_node.h"
#include "connection.h"
#include "ast_builder.h"
#include "assembly_generator_chip32_tac.h"
#include "chip32_machine.h"
#include "variable.h"
// ===================================================================
// TEST 1 : BranchNode avec condition booléenne directe (variable)
// ===================================================================
TEST_CASE("Branch with direct boolean variable", "[branch][boolean][variable]") {
std::cout << "\n=== Test: Branch with direct boolean variable ===\n";
// Variables
std::vector<std::shared_ptr<Variable>> variables;
// Condition = 1 (true)
auto varCondition = std::make_shared<Variable>("condition");
varCondition->SetIntegerValue(1);
variables.push_back(varCondition);
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto varNode = std::make_shared<VariableNode>("var-condition");
varNode->SetVariable(varCondition);
auto branchNode = std::make_shared<BranchNode>("branch-node");
branchNode->Initialize();
auto printTrue = std::make_shared<PrintNode>("print-true");
printTrue->SetText("TRUE branch executed");
printTrue->Initialize();
auto printFalse = std::make_shared<PrintNode>("print-false");
printFalse->SetText("FALSE branch executed");
printFalse->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry,
varNode,
branchNode,
printTrue,
printFalse
};
// Connections
std::vector<std::shared_ptr<Connection>> connections;
// Execution: Entry → Branch
auto execConn = std::make_shared<Connection>();
execConn->outNodeId = functionEntry->GetId();
execConn->outPortIndex = 0;
execConn->inNodeId = branchNode->GetId();
execConn->inPortIndex = 0;
execConn->type = Connection::EXECUTION_LINK;
connections.push_back(execConn);
// Data: Variable → Branch.condition (port 1)
auto dataConn = std::make_shared<Connection>();
dataConn->outNodeId = varNode->GetId();
dataConn->outPortIndex = 0;
dataConn->inNodeId = branchNode->GetId();
dataConn->inPortIndex = 1;
dataConn->type = Connection::DATA_LINK;
connections.push_back(dataConn);
// Execution: Branch.true → PrintTrue
auto execTrue = std::make_shared<Connection>();
execTrue->outNodeId = branchNode->GetId();
execTrue->outPortIndex = 0;
execTrue->inNodeId = printTrue->GetId();
execTrue->inPortIndex = 0;
execTrue->type = Connection::EXECUTION_LINK;
connections.push_back(execTrue);
// Execution: Branch.false → PrintFalse
auto execFalse = std::make_shared<Connection>();
execFalse->outNodeId = branchNode->GetId();
execFalse->outPortIndex = 1;
execFalse->inNodeId = printFalse->GetId();
execFalse->inPortIndex = 0;
execFalse->type = Connection::EXECUTION_LINK;
connections.push_back(execFalse);
// Build AST
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
// Generate Assembly using TAC
AssemblyGenerator::GeneratorContext context(
variables,
"2025-01-10 10:00:00",
"test-branch-bool-var",
true,
true,
1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
std::cout << "\n--- Generated Assembly ---\n";
std::cout << assembly << std::endl;
// Verify
REQUIRE(assembly.find("DATA") != std::string::npos);
REQUIRE(assembly.find("CODE") != std::string::npos);
REQUIRE(assembly.find("branch_true") != std::string::npos);
REQUIRE(assembly.find("branch_false") != std::string::npos);
// Execute
std::cout << "\n--- Execution Output ---\n";
std::cout << "Expected: TRUE branch executed (condition = 1)\n";
std::cout << "Actual: ";
Chip32::Machine machine;
machine.QuickExecute(assembly);
std::cout << "\n✓ Test passed\n";
}
// ===================================================================
// TEST 2 : BranchNode avec OperatorNode (comparaison)
// ===================================================================
TEST_CASE("Branch with OperatorNode comparison", "[branch][operator][comparison]") {
std::cout << "\n=== Test: Branch with OperatorNode (A > B) ===\n";
// Variables
std::vector<std::shared_ptr<Variable>> variables;
auto varA = std::make_shared<Variable>("A");
varA->SetIntegerValue(10);
variables.push_back(varA);
auto varB = std::make_shared<Variable>("B");
varB->SetIntegerValue(5);
variables.push_back(varB);
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto varNodeA = std::make_shared<VariableNode>("var-A");
varNodeA->SetVariable(varA);
auto varNodeB = std::make_shared<VariableNode>("var-B");
varNodeB->SetVariable(varB);
auto operatorNode = std::make_shared<OperatorNode>("operator-gt");
operatorNode->SetOperationType(OperatorNode::OperationType::GREATER_THAN);
operatorNode->Initialize();
auto branchNode = std::make_shared<BranchNode>("branch-node");
branchNode->Initialize();
auto printTrue = std::make_shared<PrintNode>("print-true");
printTrue->SetText("A is greater than B");
printTrue->Initialize();
auto printFalse = std::make_shared<PrintNode>("print-false");
printFalse->SetText("A is NOT greater than B");
printFalse->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry,
varNodeA,
varNodeB,
operatorNode,
branchNode,
printTrue,
printFalse
};
// Connections
std::vector<std::shared_ptr<Connection>> connections;
// Execution: Entry → Branch
auto execConn = std::make_shared<Connection>();
execConn->outNodeId = functionEntry->GetId();
execConn->outPortIndex = 0;
execConn->inNodeId = branchNode->GetId();
execConn->inPortIndex = 0;
execConn->type = Connection::EXECUTION_LINK;
connections.push_back(execConn);
// Data: A → Operator.in1
auto dataConn1 = std::make_shared<Connection>();
dataConn1->outNodeId = varNodeA->GetId();
dataConn1->outPortIndex = 0;
dataConn1->inNodeId = operatorNode->GetId();
dataConn1->inPortIndex = 0;
dataConn1->type = Connection::DATA_LINK;
connections.push_back(dataConn1);
// Data: B → Operator.in2
auto dataConn2 = std::make_shared<Connection>();
dataConn2->outNodeId = varNodeB->GetId();
dataConn2->outPortIndex = 0;
dataConn2->inNodeId = operatorNode->GetId();
dataConn2->inPortIndex = 1;
dataConn2->type = Connection::DATA_LINK;
connections.push_back(dataConn2);
// Data: Operator.out → Branch.condition
auto dataConn3 = std::make_shared<Connection>();
dataConn3->outNodeId = operatorNode->GetId();
dataConn3->outPortIndex = 0;
dataConn3->inNodeId = branchNode->GetId();
dataConn3->inPortIndex = 1;
dataConn3->type = Connection::DATA_LINK;
connections.push_back(dataConn3);
// Execution: Branch.true → PrintTrue
auto execTrue = std::make_shared<Connection>();
execTrue->outNodeId = branchNode->GetId();
execTrue->outPortIndex = 0;
execTrue->inNodeId = printTrue->GetId();
execTrue->inPortIndex = 0;
execTrue->type = Connection::EXECUTION_LINK;
connections.push_back(execTrue);
// Execution: Branch.false → PrintFalse
auto execFalse = std::make_shared<Connection>();
execFalse->outNodeId = branchNode->GetId();
execFalse->outPortIndex = 1;
execFalse->inNodeId = printFalse->GetId();
execFalse->inPortIndex = 0;
execFalse->type = Connection::EXECUTION_LINK;
connections.push_back(execFalse);
// Build and generate
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-10", "test-branch-operator", true, true, 1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
std::cout << "\n--- Generated Assembly ---\n";
std::cout << assembly << std::endl;
// Verify TAC structure
REQUIRE(assembly.find("gt") != std::string::npos); // Greater Than opcode
REQUIRE(assembly.find("branch_true") != std::string::npos);
REQUIRE(assembly.find("branch_false") != std::string::npos);
// Execute
std::cout << "\n--- Execution Output ---\n";
std::cout << "Expected: A is greater than B (10 > 5)\n";
std::cout << "Actual: ";
Chip32::Machine machine;
machine.QuickExecute(assembly);
std::cout << "\n✓ Test passed\n";
}
// ===================================================================
// TEST 3 : Tester différentes valeurs (0 = false, autres = true)
// ===================================================================
TEST_CASE("Branch with various condition values", "[branch][values][convention]") {
std::cout << "\n=== Test: Branch with various condition values ===\n";
struct TestCase {
int conditionValue;
bool expectedTrue;
std::string description;
};
std::vector<TestCase> testCases = {
{0, false, "0 should be FALSE"},
{1, true, "1 should be TRUE"},
{42, true, "42 should be TRUE"},
{-5, true, "-5 should be TRUE"},
{100, true, "100 should be TRUE"}
};
for (const auto& test : testCases) {
std::cout << "\n--- Testing: " << test.description << " ---\n";
// Variables
std::vector<std::shared_ptr<Variable>> variables;
auto varCondition = std::make_shared<Variable>("condition");
varCondition->SetIntegerValue(test.conditionValue);
variables.push_back(varCondition);
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto varNode = std::make_shared<VariableNode>("var-condition");
varNode->SetVariable(varCondition);
auto branchNode = std::make_shared<BranchNode>("branch-node");
branchNode->Initialize();
auto printTrue = std::make_shared<PrintNode>("print-true");
printTrue->SetText("TRUE");
printTrue->Initialize();
auto printFalse = std::make_shared<PrintNode>("print-false");
printFalse->SetText("FALSE");
printFalse->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry, varNode, branchNode, printTrue, printFalse
};
// Connections (même pattern que TEST 1)
std::vector<std::shared_ptr<Connection>> connections;
auto execConn = std::make_shared<Connection>();
execConn->outNodeId = functionEntry->GetId();
execConn->outPortIndex = 0;
execConn->inNodeId = branchNode->GetId();
execConn->inPortIndex = 0;
execConn->type = Connection::EXECUTION_LINK;
connections.push_back(execConn);
auto dataConn = std::make_shared<Connection>();
dataConn->outNodeId = varNode->GetId();
dataConn->outPortIndex = 0;
dataConn->inNodeId = branchNode->GetId();
dataConn->inPortIndex = 1;
dataConn->type = Connection::DATA_LINK;
connections.push_back(dataConn);
auto execTrue = std::make_shared<Connection>();
execTrue->outNodeId = branchNode->GetId();
execTrue->outPortIndex = 0;
execTrue->inNodeId = printTrue->GetId();
execTrue->inPortIndex = 0;
execTrue->type = Connection::EXECUTION_LINK;
connections.push_back(execTrue);
auto execFalse = std::make_shared<Connection>();
execFalse->outNodeId = branchNode->GetId();
execFalse->outPortIndex = 1;
execFalse->inNodeId = printFalse->GetId();
execFalse->inPortIndex = 0;
execFalse->type = Connection::EXECUTION_LINK;
connections.push_back(execFalse);
// Build and generate
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-10", "test-branch-values", false, true, 1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
// Execute
Chip32::Machine machine;
machine.QuickExecute(assembly);
std::string output = machine.printOutput;
std::string expected = test.expectedTrue ? "TRUE" : "FALSE";
std::cout << "Condition value: " << test.conditionValue << "\n";
std::cout << "Expected: " << expected << ", Got: " << output << "\n";
REQUIRE(output.find(expected) != std::string::npos);
}
std::cout << "\n✓ All value tests passed\n";
}
// ===================================================================
// TEST 4 : Tester tous les opérateurs de comparaison
// ===================================================================
TEST_CASE("Branch with all comparison operators", "[branch][operators][all]") {
std::cout << "\n=== Test: All comparison operators ===\n";
struct TestCase {
OperatorNode::OperationType op;
int a, b;
bool expectedTrue;
std::string description;
};
std::vector<TestCase> testCases = {
{OperatorNode::OperationType::EQUAL, 5, 5, true, "5 == 5"},
{OperatorNode::OperationType::EQUAL, 5, 10, false, "5 == 10"},
{OperatorNode::OperationType::NOT_EQUAL, 5, 10, true, "5 != 10"},
{OperatorNode::OperationType::NOT_EQUAL, 5, 5, false, "5 != 5"},
{OperatorNode::OperationType::GREATER_THAN, 10, 5, true, "10 > 5"},
{OperatorNode::OperationType::GREATER_THAN, 5, 10, false, "5 > 10"},
{OperatorNode::OperationType::LESS_THAN, 5, 10, true, "5 < 10"},
{OperatorNode::OperationType::LESS_THAN, 10, 5, false, "10 < 5"},
{OperatorNode::OperationType::GREATER_EQUAL, 10, 5, true, "10 >= 5"},
{OperatorNode::OperationType::GREATER_EQUAL, 5, 5, true, "5 >= 5"},
{OperatorNode::OperationType::GREATER_EQUAL, 5, 10, false, "5 >= 10"},
{OperatorNode::OperationType::LESS_EQUAL, 5, 10, true, "5 <= 10"},
{OperatorNode::OperationType::LESS_EQUAL, 5, 5, true, "5 <= 5"},
{OperatorNode::OperationType::LESS_EQUAL, 10, 5, false, "10 <= 5"}
};
for (const auto& test : testCases) {
std::cout << "\n--- Testing: " << test.description << " ---\n";
// Variables
std::vector<std::shared_ptr<Variable>> variables;
auto varA = std::make_shared<Variable>("A");
varA->SetIntegerValue(test.a);
variables.push_back(varA);
auto varB = std::make_shared<Variable>("B");
varB->SetIntegerValue(test.b);
variables.push_back(varB);
// Nodes (même structure que TEST 2)
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto varNodeA = std::make_shared<VariableNode>("var-A");
varNodeA->SetVariable(varA);
auto varNodeB = std::make_shared<VariableNode>("var-B");
varNodeB->SetVariable(varB);
auto operatorNode = std::make_shared<OperatorNode>("operator");
operatorNode->SetOperationType(test.op);
operatorNode->Initialize();
auto branchNode = std::make_shared<BranchNode>("branch-node");
branchNode->Initialize();
auto printTrue = std::make_shared<PrintNode>("print-true");
printTrue->SetText("TRUE");
printTrue->Initialize();
auto printFalse = std::make_shared<PrintNode>("print-false");
printFalse->SetText("FALSE");
printFalse->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry, varNodeA, varNodeB, operatorNode, branchNode, printTrue, printFalse
};
// Connections (pattern complet)
std::vector<std::shared_ptr<Connection>> connections;
auto execConn = std::make_shared<Connection>();
execConn->outNodeId = functionEntry->GetId();
execConn->outPortIndex = 0;
execConn->inNodeId = branchNode->GetId();
execConn->inPortIndex = 0;
execConn->type = Connection::EXECUTION_LINK;
connections.push_back(execConn);
auto dataConn1 = std::make_shared<Connection>();
dataConn1->outNodeId = varNodeA->GetId();
dataConn1->outPortIndex = 0;
dataConn1->inNodeId = operatorNode->GetId();
dataConn1->inPortIndex = 0;
dataConn1->type = Connection::DATA_LINK;
connections.push_back(dataConn1);
auto dataConn2 = std::make_shared<Connection>();
dataConn2->outNodeId = varNodeB->GetId();
dataConn2->outPortIndex = 0;
dataConn2->inNodeId = operatorNode->GetId();
dataConn2->inPortIndex = 1;
dataConn2->type = Connection::DATA_LINK;
connections.push_back(dataConn2);
auto dataConn3 = std::make_shared<Connection>();
dataConn3->outNodeId = operatorNode->GetId();
dataConn3->outPortIndex = 0;
dataConn3->inNodeId = branchNode->GetId();
dataConn3->inPortIndex = 1;
dataConn3->type = Connection::DATA_LINK;
connections.push_back(dataConn3);
auto execTrue = std::make_shared<Connection>();
execTrue->outNodeId = branchNode->GetId();
execTrue->outPortIndex = 0;
execTrue->inNodeId = printTrue->GetId();
execTrue->inPortIndex = 0;
execTrue->type = Connection::EXECUTION_LINK;
connections.push_back(execTrue);
auto execFalse = std::make_shared<Connection>();
execFalse->outNodeId = branchNode->GetId();
execFalse->outPortIndex = 1;
execFalse->inNodeId = printFalse->GetId();
execFalse->inPortIndex = 0;
execFalse->type = Connection::EXECUTION_LINK;
connections.push_back(execFalse);
// Build and execute
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-10", "test-branch-op", false, true, 1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
Chip32::Machine machine;
machine.QuickExecute(assembly);
std::string output = machine.printOutput;
std::string expected = test.expectedTrue ? "TRUE" : "FALSE";
std::cout << "Expected: " << expected << ", Got: " << output << "\n";
REQUIRE(output.find(expected) != std::string::npos);
}
std::cout << "\n✓ All operator tests passed\n";
}
// ===================================================================
// TEST 5 : BranchNode avec résultat d'opération arithmétique
// ===================================================================
TEST_CASE("Branch with arithmetic operation result", "[branch][arithmetic]") {
std::cout << "\n=== Test: Branch with arithmetic result (5 - 5 = 0) ===\n";
// Variables
std::vector<std::shared_ptr<Variable>> variables;
auto varA = std::make_shared<Variable>("A");
varA->SetIntegerValue(5);
variables.push_back(varA);
auto varB = std::make_shared<Variable>("B");
varB->SetIntegerValue(5);
variables.push_back(varB);
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto varNodeA = std::make_shared<VariableNode>("var-A");
varNodeA->SetVariable(varA);
auto varNodeB = std::make_shared<VariableNode>("var-B");
varNodeB->SetVariable(varB);
// Soustraction: 5 - 5 = 0 (false)
auto subNode = std::make_shared<OperatorNode>("operator-sub");
subNode->SetOperationType(OperatorNode::OperationType::SUBTRACT);
subNode->Initialize();
auto branchNode = std::make_shared<BranchNode>("branch-node");
branchNode->Initialize();
auto printTrue = std::make_shared<PrintNode>("print-true");
printTrue->SetText("Result is NON-ZERO");
printTrue->Initialize();
auto printFalse = std::make_shared<PrintNode>("print-false");
printFalse->SetText("Result is ZERO");
printFalse->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry, varNodeA, varNodeB, subNode, branchNode, printTrue, printFalse
};
// Connections
std::vector<std::shared_ptr<Connection>> connections;
auto execConn = std::make_shared<Connection>();
execConn->outNodeId = functionEntry->GetId();
execConn->outPortIndex = 0;
execConn->inNodeId = branchNode->GetId();
execConn->inPortIndex = 0;
execConn->type = Connection::EXECUTION_LINK;
connections.push_back(execConn);
auto dataConn1 = std::make_shared<Connection>();
dataConn1->outNodeId = varNodeA->GetId();
dataConn1->outPortIndex = 0;
dataConn1->inNodeId = subNode->GetId();
dataConn1->inPortIndex = 0;
dataConn1->type = Connection::DATA_LINK;
connections.push_back(dataConn1);
auto dataConn2 = std::make_shared<Connection>();
dataConn2->outNodeId = varNodeB->GetId();
dataConn2->outPortIndex = 0;
dataConn2->inNodeId = subNode->GetId();
dataConn2->inPortIndex = 1;
dataConn2->type = Connection::DATA_LINK;
connections.push_back(dataConn2);
auto dataConn3 = std::make_shared<Connection>();
dataConn3->outNodeId = subNode->GetId();
dataConn3->outPortIndex = 0;
dataConn3->inNodeId = branchNode->GetId();
dataConn3->inPortIndex = 1;
dataConn3->type = Connection::DATA_LINK;
connections.push_back(dataConn3);
auto execTrue = std::make_shared<Connection>();
execTrue->outNodeId = branchNode->GetId();
execTrue->outPortIndex = 0;
execTrue->inNodeId = printTrue->GetId();
execTrue->inPortIndex = 0;
execTrue->type = Connection::EXECUTION_LINK;
connections.push_back(execTrue);
auto execFalse = std::make_shared<Connection>();
execFalse->outNodeId = branchNode->GetId();
execFalse->outPortIndex = 1;
execFalse->inNodeId = printFalse->GetId();
execFalse->inPortIndex = 0;
execFalse->type = Connection::EXECUTION_LINK;
connections.push_back(execFalse);
// Build and execute
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-10", "test-branch-arithmetic", true, true, 1024
);
AssemblyGeneratorChip32TAC tacGen(context);
tacGen.GenerateCompleteProgram(nodes, pathTree);
std::string assembly = tacGen.GetAssembly().str();
std::cout << "\n--- Generated Assembly ---\n";
std::cout << assembly << std::endl;
// Verify subtraction is present
REQUIRE(assembly.find("sub") != std::string::npos);
// Execute
std::cout << "\n--- Execution Output ---\n";
std::cout << "Expected: Result is ZERO (5 - 5 = 0)\n";
std::cout << "Actual: ";
Chip32::Machine machine;
machine.QuickExecute(assembly);
std::string output = machine.printOutput;
REQUIRE(output.find("ZERO") != std::string::npos);
std::cout << "\n✓ Test passed\n";
}

View file

@ -148,6 +148,7 @@ set(SRCS
src/node_editor/operator_node_widget.cpp
src/node_editor/print_node_widget.cpp
src/node_editor/syscall_node_widget.cpp
src/node_editor/branch_node_widget.cpp
src/gui.cpp
src/media_converter.cpp

View file

@ -1,6 +1,6 @@
[Window][WindowOverViewport_11111111]
Pos=60,26
Size=1220,836
Size=1429,832
Collapsed=0
[Window][Debug##Default]
@ -9,32 +9,32 @@ Size=400,400
Collapsed=0
[Window][Library Manager]
Pos=630,26
Size=650,518
Pos=554,26
Size=935,607
Collapsed=0
DockId=0x00000002,0
[Window][Console]
Pos=60,546
Size=628,316
Pos=60,635
Size=618,223
Collapsed=0
DockId=0x00000004,0
[Window][Emulator]
Pos=630,26
Size=650,518
Pos=554,26
Size=935,607
Collapsed=0
DockId=0x00000002,5
[Window][Code viewer]
Pos=630,26
Size=650,518
Pos=554,26
Size=935,607
Collapsed=0
DockId=0x00000002,4
[Window][Resources]
Pos=630,26
Size=650,518
Pos=554,26
Size=935,607
Collapsed=0
DockId=0x00000002,1
@ -50,36 +50,36 @@ Size=150,42
Collapsed=0
[Window][Variables]
Pos=690,546
Size=590,316
Pos=680,635
Size=809,223
Collapsed=0
DockId=0x00000005,0
[Window][CPU]
Pos=630,26
Size=650,518
Pos=554,26
Size=935,607
Collapsed=0
DockId=0x00000002,2
[Window][RAM view]
Pos=630,26
Size=650,518
Pos=554,26
Size=935,607
Collapsed=0
DockId=0x00000002,3
[Window][Properties]
Pos=690,546
Size=590,316
Pos=680,635
Size=809,223
Collapsed=0
DockId=0x00000005,1
[Window][ToolBar]
Pos=0,26
Size=60,836
Size=60,832
Collapsed=0
[Window][QuitConfirm]
Pos=508,383
Pos=508,312
Size=264,96
Collapsed=0
@ -90,13 +90,13 @@ Collapsed=0
[Window][Module editor]
Pos=60,26
Size=568,518
Size=492,607
Collapsed=0
DockId=0x00000001,0
[Window][Story editor]
Pos=60,26
Size=568,518
Size=492,607
Collapsed=0
DockId=0x00000001,1
@ -106,8 +106,8 @@ Size=687,422
Collapsed=0
[Window][Error List]
Pos=60,546
Size=628,316
Pos=60,635
Size=618,223
Collapsed=0
DockId=0x00000004,1
@ -155,11 +155,11 @@ Column 2 Weight=1.0000
Column 3 Width=60
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,836 Split=Y
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,549 Split=X
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=1144,694 CentralNode=1 Selected=0x93ADCAAB
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=650,694 Selected=0xE5897A33
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,316 Split=X Selected=0xEA83D666
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=616,192 Selected=0xEA83D666
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=578,192 Selected=0x8C72BEA8
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1429,832 Split=Y
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,469 Split=X
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=492,694 CentralNode=1 Selected=0x93ADCAAB
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=935,694 Selected=0x63869CAF
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,223 Split=X Selected=0xEA83D666
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=621,192 Selected=0xEA83D666
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=813,192 Selected=0x8C72BEA8

View file

@ -34,7 +34,7 @@
#include "print_node_widget.h"
#include "syscall_node_widget.h"
#include "function_entry_widget.h"
#include "branch_node_widget.h"
MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appController)
: m_logger(logger)
@ -74,6 +74,7 @@ MainWindow::MainWindow(ILogger& logger, EventBus& eventBus, AppController& appCo
m_widgetFactory.registerNode<PrintNodeWidget>(PrintNodeUuid);
m_widgetFactory.registerNode<SyscallNodeWidget>(SyscallNodeUuid);
m_widgetFactory.registerNode<FunctionEntryWidget>(FunctionEntryNodeUuid);
m_widgetFactory.registerNode<BranchNodeWidget>(BranchNodeUuid);
m_eventBus.Subscribe<OpenProjectEvent>([this](const OpenProjectEvent &event) {
OpenProject(event.GetUuid());

View file

@ -0,0 +1,54 @@
#include "branch_node_widget.h"
#include <sstream>
#include "IconsMaterialDesignIcons.h"
#include "story_project.h"
BranchNodeWidget::BranchNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> node)
: BaseNodeWidget(manager, node)
{
m_branchNode = std::dynamic_pointer_cast<BranchNode>(node);
}
void BranchNodeWidget::Initialize()
{
BaseNodeWidget::Initialize();
}
void BranchNodeWidget::DrawProperties(std::shared_ptr<IStoryProject> story)
{
ImGui::AlignTextToFramePadding();
// Informations sur le nœud de branchement
ImGui::SeparatorText("Branch Node Properties");
ImGui::TextWrapped("This node evaluates a boolean condition and executes "
"one of two paths based on the result.");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
}
void BranchNodeWidget::Draw()
{
// Icône et texte du nœud
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.8f, 0.2f, 1.0f));
ImGui::Text(ICON_MDI_SOURCE_BRANCH " Branch");
ImGui::PopStyleColor();
ImGui::Spacing();
// Affichage de la condition
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, 1.0f), "if (condition)");
ImGui::Spacing();
// Affichage visuel des branches
ImGui::BeginGroup();
{
ImGui::TextColored(ImVec4(0.3f, 1.0f, 0.3f, 1.0f), ICON_MDI_CHECK " TRUE");
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), ICON_MDI_CLOSE " FALSE");
}
ImGui::EndGroup();
}

View file

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include <map>
#include <memory>
#include "base_node_widget.h"
#include "i_story_manager.h"
#include "i_story_project.h"
#include "gui.h"
#include "branch_node.h"
/**
* @brief Widget pour le nœud Branch (If/Else)
*
* Gère l'affichage et les propriétés d'un nœud de branchement conditionnel simple.
* La condition est évaluée selon la convention: 0 = false, toute autre valeur = true
*
* Ports:
* - Entrée 0: Flux d'exécution
* - Entrée 1: Condition booléenne (provient généralement d'un OperatorNode)
* - Sortie 0: Branche TRUE (condition != 0)
* - Sortie 1: Branche FALSE (condition == 0)
*/
class BranchNodeWidget : public BaseNodeWidget
{
public:
BranchNodeWidget(IStoryManager &manager, std::shared_ptr<BaseNode> node);
void Draw() override;
void DrawProperties(std::shared_ptr<IStoryProject> story) override;
void Initialize() override;
private:
std::shared_ptr<BranchNode> m_branchNode;
};