mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
merged tests all in core now, added print test
This commit is contained in:
parent
88b9bc6b3e
commit
9c6df3c9b6
17 changed files with 493 additions and 18482 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -85,5 +85,5 @@ story-editor/.flatpak-builder
|
|||
story-editor/flatpak_build/
|
||||
story-editor/AppDir/
|
||||
|
||||
core/story-manager/tests/build
|
||||
story-player/android/build/reports/problems/problems-report.html
|
||||
core/tests/build
|
||||
34
.vscode/launch.json
vendored
34
.vscode/launch.json
vendored
|
|
@ -69,41 +69,13 @@
|
|||
|
||||
},
|
||||
{
|
||||
"name": "Debug VM Tests",
|
||||
"name": "Debug tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/core/chip32/tests/build/chip32_test", // Remplacez par le chemin de votre exécutable
|
||||
"program": "${workspaceFolder}/core/tests/build/core_tests", // Remplacez par le chemin de votre exécutable
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/core/chip32/tests",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"linux": {
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "/usr/bin/gdb"
|
||||
},
|
||||
"osx": {
|
||||
"MIMode": "lldb",
|
||||
"miDebuggerPath": "/Users/user936511/.vscode/extensions/ms-vscode.cpptools-1.18.5-darwin-arm64/debugAdapters/lldb-mi/bin/lldb-mi"
|
||||
},
|
||||
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
{
|
||||
"name": "Debug Compiler AST Tests",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/core/story-manager/tests/build/compiler_test", // Remplacez par le chemin de votre exécutable
|
||||
"args": [],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}/core/story-manager/tests/build",
|
||||
"cwd": "${workspaceFolder}/core/tests/build",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"linux": {
|
||||
|
|
|
|||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -1,8 +1,7 @@
|
|||
{
|
||||
|
||||
"cmake.sourceDirectory": [
|
||||
"${workspaceFolder}/core/chip32/tests",
|
||||
"${workspaceFolder}/core/story-manager/tests",
|
||||
"${workspaceFolder}/core/tests",
|
||||
"${workspaceFolder}/story-editor",
|
||||
"${workspaceFolder}/story-player-raylib",
|
||||
"${workspaceFolder}/software"
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(chip32_test LANGUAGES CXX C)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(chip32_test
|
||||
main.cpp
|
||||
test_parser.cpp
|
||||
test_vm.cpp
|
||||
../chip32_assembler.cpp
|
||||
../chip32_vm.c
|
||||
)
|
||||
|
||||
target_include_directories(chip32_test PRIVATE ../)
|
||||
|
||||
install(TARGETS chip32_test
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
#include "ast_builder.h"
|
||||
#include "assembly_generator.h"
|
||||
#include "call_function_node.h"
|
||||
#include <algorithm>
|
||||
|
||||
class AssemblyGeneratorChip32 : public AssemblyGenerator
|
||||
{
|
||||
|
|
@ -137,19 +138,97 @@ private:
|
|||
m_depth--;
|
||||
}
|
||||
|
||||
void GeneratePrintNode(std::shared_ptr<ASTNode> node) {
|
||||
void GeneratePrintNode(std::shared_ptr<ASTNode> node)
|
||||
{
|
||||
auto* printNode = node->GetAs<PrintNode>();
|
||||
if (!printNode) return;
|
||||
|
||||
std::string label = printNode->GetLabel();
|
||||
|
||||
AddComment("Print: " + printNode->GetText());
|
||||
m_depth++;
|
||||
|
||||
// Count the number of arguments connected to the print node
|
||||
int argCount = 0;
|
||||
std::vector<std::pair<unsigned int, std::shared_ptr<ASTNode>>> sortedInputs;
|
||||
|
||||
// Collect and sort data inputs by port index
|
||||
for (const auto& [port, inputNode] : node->dataInputs) {
|
||||
sortedInputs.push_back({port, inputNode});
|
||||
}
|
||||
|
||||
// Sort by port index to ensure correct argument order (arg0, arg1, arg2, arg3)
|
||||
std::sort(sortedInputs.begin(), sortedInputs.end(),
|
||||
[](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
|
||||
argCount = sortedInputs.size();
|
||||
|
||||
// Save registers that we'll use
|
||||
m_assembly << " push r0\n"
|
||||
<< " push r1\n"
|
||||
<< " lcons r0, $" << label << "\n"
|
||||
<< " lcons r1, 0 ; number of arguments\n" // FIXME: handle arguments
|
||||
<< " syscall 4\n"
|
||||
<< " pop r1\n"
|
||||
<< " push r1\n";
|
||||
|
||||
// Save argument registers if we have arguments
|
||||
if (argCount > 0) m_assembly << " push r2\n";
|
||||
if (argCount > 1) m_assembly << " push r3\n";
|
||||
if (argCount > 2) m_assembly << " push r4\n";
|
||||
if (argCount > 3) m_assembly << " push r5\n";
|
||||
|
||||
// Load arguments into registers r2, r3, r4, r5
|
||||
int regIndex = 2; // Start with r2 for first argument
|
||||
for (const auto& [port, inputNode] : sortedInputs) {
|
||||
if (regIndex > 5) {
|
||||
// Maximum 4 arguments (r2, r3, r4, r5)
|
||||
throw std::runtime_error("Print node supports maximum 4 arguments");
|
||||
}
|
||||
|
||||
// Check if the input node is a variable
|
||||
if (inputNode->IsType<VariableNode>()) {
|
||||
auto* varNode = inputNode->GetAs<VariableNode>();
|
||||
if (varNode) {
|
||||
std::string varUuid = varNode->GetVariableUuid();
|
||||
|
||||
// Find variable in the context
|
||||
auto var = m_context.FindVariableByUuid(varUuid);
|
||||
if (var) {
|
||||
m_assembly << " ; Load arg" << (regIndex - 2) << ": "
|
||||
<< var->GetVariableName() << "\n";
|
||||
int varSize = GetVariableSize(var);
|
||||
m_assembly << " load r" << regIndex << ", $"
|
||||
<< var->GetLabel() << ", " << varSize << " ; "
|
||||
<< var->GetVariableName() << "\n";
|
||||
} else {
|
||||
throw std::runtime_error("Variable not found in context for print argument");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For non-variable inputs, we might need to evaluate expressions
|
||||
// For now, we only support direct variable connections
|
||||
throw std::runtime_error("Print node currently only supports direct variable connections");
|
||||
}
|
||||
|
||||
regIndex++;
|
||||
}
|
||||
|
||||
// Load format string address into r0
|
||||
m_assembly << " lcons r0, $" << label << " ; format string\n";
|
||||
|
||||
// Load number of arguments into r1
|
||||
m_assembly << " lcons r1, " << argCount << " ; number of arguments\n";
|
||||
|
||||
// Call printf syscall
|
||||
m_assembly << " syscall 4\n";
|
||||
|
||||
// Restore argument registers (in reverse order)
|
||||
if (argCount > 3) m_assembly << " pop r5\n";
|
||||
if (argCount > 2) m_assembly << " pop r4\n";
|
||||
if (argCount > 1) m_assembly << " pop r3\n";
|
||||
if (argCount > 0) m_assembly << " pop r2\n";
|
||||
|
||||
// Restore r0 and r1
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n";
|
||||
|
||||
m_depth--;
|
||||
}
|
||||
|
||||
void GenerateOperatorNode(std::shared_ptr<ASTNode> node) {
|
||||
|
|
@ -175,157 +254,183 @@ private:
|
|||
if (var)
|
||||
{
|
||||
// Generate code to load the variable value
|
||||
// FIXME: hardcoded 4 bytes, replace by actual real variable size
|
||||
m_assembly << " load r" << reg << ", $" << var->GetLabel() << ", 4" << " ; Load variable " << var->GetVariableName() << "\n";
|
||||
int varSize = GetVariableSize(var);
|
||||
m_assembly << " load r" << reg << ", $" << var->GetLabel()
|
||||
<< ", " << varSize << " ; Load variable "
|
||||
<< var->GetVariableName() << "\n";
|
||||
m_assembly << " push r" << reg << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("ERROR! Variable not set in node: " + inputNode->node->GetId());
|
||||
throw std::runtime_error("Variable not set in node: " + inputNode->node->GetId());
|
||||
}
|
||||
}
|
||||
reg++;
|
||||
}
|
||||
reg++;
|
||||
}
|
||||
|
||||
// Generate operator code based on type
|
||||
switch (opNode->GetOperationType()) {
|
||||
// ===== ARITHMETIC OPERATORS =====
|
||||
case OperatorNode::OperationType::ADD:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " add r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
case OperatorNode::OperationType::SUBTRACT:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " sub r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
case OperatorNode::OperationType::MULTIPLY:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " mul r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
case OperatorNode::OperationType::DIVIDE:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " div r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
case OperatorNode::OperationType::MODULO:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " mod r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
// ===== COMPARISON OPERATORS =====
|
||||
// Utilise les instructions eq, gt, lt de Chip32
|
||||
// Syntaxe: eq r_dest, r_op1, r_op2 → r_dest = (r_op1 == r_op2 ? 1 : 0)
|
||||
|
||||
case OperatorNode::OperationType::EQUAL:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " cmp r0, r1\n"
|
||||
<< " lcons r0, 1\n"
|
||||
<< " skipz\n"
|
||||
<< " lcons r0, 0\n"
|
||||
m_assembly << " pop r1 ; second operand\n"
|
||||
<< " pop r0 ; first operand\n"
|
||||
<< " eq r0, r0, r1 ; r0 = (r0 == r1 ? 1 : 0)\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::NOT_EQUAL:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " cmp r0, r1\n"
|
||||
<< " lcons r0, 0\n"
|
||||
<< " skipz\n"
|
||||
<< " lcons r0, 1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " eq r0, r0, r1 ; r0 = (r0 == r1 ? 1 : 0)\n"
|
||||
<< " lcons r2, 1\n"
|
||||
<< " xor r0, r2 ; inverse: 0→1, 1→0\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::GREATER_THAN:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " cmp r0, r1\n"
|
||||
<< " lcons r0, 1\n"
|
||||
<< " skipgt\n"
|
||||
<< " lcons r0, 0\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " gt r0, r0, r1 ; r0 = (r0 > r1 ? 1 : 0)\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::LESS_THAN:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " cmp r0, r1\n"
|
||||
<< " lcons r0, 1\n"
|
||||
<< " skiplt\n"
|
||||
<< " lcons r0, 0\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " lt r0, r0, r1 ; r0 = (r0 < r1 ? 1 : 0)\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::GREATER_EQUAL:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " cmp r0, r1\n"
|
||||
<< " lcons r0, 1\n"
|
||||
<< " skipge\n"
|
||||
<< " lcons r0, 0\n"
|
||||
// >= est équivalent à NOT(<)
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " lt r0, r0, r1 ; r0 = (r0 < r1 ? 1 : 0)\n"
|
||||
<< " lcons r2, 1\n"
|
||||
<< " xor r0, r2 ; inverse: >= est NOT(<)\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::LESS_EQUAL:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " cmp r0, r1\n"
|
||||
<< " lcons r0, 1\n"
|
||||
<< " skiple\n"
|
||||
<< " lcons r0, 0\n"
|
||||
// <= est équivalent à NOT(>)
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " gt r0, r0, r1 ; r0 = (r0 > r1 ? 1 : 0)\n"
|
||||
<< " lcons r2, 1\n"
|
||||
<< " xor r0, r2 ; inverse: <= est NOT(>)\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
// ===== LOGICAL OPERATORS =====
|
||||
case OperatorNode::OperationType::AND:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " and r0, r1\n"
|
||||
// AND logique: résultat 1 si les deux sont non-zéro
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " ; Logical AND\n"
|
||||
<< " lcons r2, 0\n"
|
||||
<< " skipz r0\n"
|
||||
<< " skipz r1\n"
|
||||
<< " lcons r2, 1\n"
|
||||
<< " mov r0, r2\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::OR:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
<< " or r0, r1\n"
|
||||
// OR logique: résultat 1 si au moins un est non-zéro
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " ; Logical OR\n"
|
||||
<< " or r0, r1 ; bitwise or\n"
|
||||
<< " lcons r2, 0\n"
|
||||
<< " skipz r0\n"
|
||||
<< " lcons r2, 1\n"
|
||||
<< " mov r0, r2\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::NOT:
|
||||
// NOT logique: 0→1, non-zero→0
|
||||
m_assembly << " pop r0\n"
|
||||
<< " not r0\n"
|
||||
<< " ; Logical NOT\n"
|
||||
<< " lcons r1, 1\n"
|
||||
<< " skipz r0\n"
|
||||
<< " lcons r1, 0\n"
|
||||
<< " mov r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
// ===== BITWISE OPERATORS =====
|
||||
case OperatorNode::OperationType::BITWISE_AND:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " and r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::BITWISE_OR:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " or r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::BITWISE_XOR:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " xor r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::BITWISE_NOT:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " not r0\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::LEFT_SHIFT:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " shl r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
case OperatorNode::OperationType::RIGHT_SHIFT:
|
||||
m_assembly << " pop r0\n"
|
||||
<< " pop r1\n"
|
||||
m_assembly << " pop r1\n"
|
||||
<< " pop r0\n"
|
||||
<< " shr r0, r1\n"
|
||||
<< " push r0\n";
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unsupported operator type");
|
||||
}
|
||||
|
|
@ -384,4 +489,21 @@ private:
|
|||
m_assembly << "$" << v->GetLabel() << " DVB, " << (v->GetValue<bool>() ? "1" : "0") << " ; " << v->GetVariableName() << "\n";
|
||||
}
|
||||
}
|
||||
private:
|
||||
// Helper pour obtenir la taille en bytes d'une variable selon son type
|
||||
int GetVariableSize(std::shared_ptr<Variable> var) const {
|
||||
switch (var->GetValueType()) {
|
||||
case Variable::ValueType::INTEGER:
|
||||
return 4; // 32 bits = 4 bytes (DV32/DC32)
|
||||
case Variable::ValueType::FLOAT:
|
||||
return 4; // 32 bits = 4 bytes (DV32/DC32)
|
||||
case Variable::ValueType::BOOL:
|
||||
return 1; // 8 bits = 1 byte (DVB/DCB)
|
||||
case Variable::ValueType::STRING:
|
||||
// Pour les strings, on charge l'adresse (pointeur 32-bit)
|
||||
return 4; // Adresse = 4 bytes
|
||||
default:
|
||||
throw std::runtime_error("Unknown variable type");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -85,9 +85,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void printAST(const AST& ast, const std::shared_ptr<AstNode>& node, const std::string& prefix = "", bool isLast = true) {
|
||||
// Afficher le nœud actuel
|
||||
std::cout << prefix;
|
||||
|
|
@ -141,68 +138,6 @@ public:
|
|||
printAST(m_ast, entryNode);
|
||||
}
|
||||
|
||||
void generateAssembly() {
|
||||
std::ostringstream assemblyCode;
|
||||
std::map<std::string, std::string> labels;
|
||||
|
||||
// Trouver le nœud de type "function-entry-node"
|
||||
std::shared_ptr<AstNode> entryNode;
|
||||
for (const auto& pair : m_ast.nodeMap) {
|
||||
if (pair.second->node->GetType() == "function-entry-node") {
|
||||
entryNode = pair.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!entryNode) {
|
||||
throw std::runtime_error("No function-entry-node found in the AST.");
|
||||
}
|
||||
|
||||
// Fonction récursive pour générer le code assembleur
|
||||
std::function<void(const std::shared_ptr<AstNode>&)> generateCode = [&](const std::shared_ptr<AstNode>& node) {
|
||||
if (node->node->GetType() == "print") {
|
||||
// Générer le code pour un nœud de type "print"
|
||||
assemblyCode << "PRINT ";
|
||||
// Supposons que le nœud print a une entrée qui est une variable
|
||||
if (!node->inputs.empty()) {
|
||||
assemblyCode << node->inputs[0]->node->GetTitle();
|
||||
}
|
||||
assemblyCode << std::endl;
|
||||
} else if (node->node->GetType() == "variable-node") {
|
||||
// Générer le code pour un nœud de type "variable-node"
|
||||
assemblyCode << "LOAD " << node->node->GetTitle() << " INTO REGISTER" << std::endl;
|
||||
} else if (node->node->GetType() == "branch-node") {
|
||||
// Générer le code pour un nœud de type "branch-node"
|
||||
assemblyCode << "BRANCH TO LABEL_" << node->node->GetId() << std::endl;
|
||||
// Générer le code pour les sorties du nœud branch
|
||||
for (const auto& outputNode : node->body) {
|
||||
generateCode(outputNode);
|
||||
}
|
||||
} else if (node->node->GetType() == "function-entry-node") {
|
||||
// Générer le code pour un nœud de type "function-entry-node"
|
||||
assemblyCode << "FUNCTION ENTRY POINT" << std::endl;
|
||||
// Générer le code pour les nœuds connectés à ce nœud
|
||||
for (const auto& inputNode : node->inputs) {
|
||||
generateCode(inputNode);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// // Generate all constants
|
||||
// for (const auto& astNode : m_ast.nodeMap) {
|
||||
// assemblyCode << astNode.second->node->GenerateConstants();
|
||||
// }
|
||||
|
||||
// After the constants, the main entry point:
|
||||
assemblyCode << ".main:\n";
|
||||
|
||||
// Commencer la génération de code à partir du nœud d'entrée
|
||||
generateCode(entryNode);
|
||||
|
||||
// Stocker le code assembleur généré dans l'AST
|
||||
m_ast.assemblyCode = assemblyCode.str();
|
||||
}
|
||||
|
||||
std::string GetCode() const {
|
||||
return m_ast.assemblyCode;
|
||||
}
|
||||
|
|
@ -211,232 +146,3 @@ private:
|
|||
AST m_ast;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
class AssemblyGenerator {
|
||||
public:
|
||||
AssemblyGenerator() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
m_assembly.str("");
|
||||
m_labelCounter = 0;
|
||||
m_variableAddresses.clear();
|
||||
m_currentStackOffset = 0;
|
||||
}
|
||||
|
||||
std::string GenerateAssembly(const std::vector<std::shared_ptr<ASTNode>>& roots) {
|
||||
Reset();
|
||||
|
||||
// Generate data section
|
||||
m_assembly << "section .data\n";
|
||||
// Add string constants for print nodes
|
||||
m_assembly << "msg_good db 'Good!',0xa,0\n";
|
||||
m_assembly << "msg_bad db 'Bad :(',0xa,0\n";
|
||||
|
||||
// Generate code section
|
||||
m_assembly << "\nsection .text\n";
|
||||
m_assembly << "global _start\n\n";
|
||||
m_assembly << "_start:\n";
|
||||
|
||||
// Generate entry point code
|
||||
for (const auto& root : roots) {
|
||||
GenerateNodeCode(root);
|
||||
}
|
||||
|
||||
// Add exit syscall
|
||||
m_assembly << " mov eax, 1\n"; // exit syscall
|
||||
m_assembly << " mov ebx, 0\n"; // return 0
|
||||
m_assembly << " int 0x80\n";
|
||||
|
||||
return m_assembly.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::stringstream m_assembly;
|
||||
int m_labelCounter;
|
||||
std::unordered_map<std::string, int> m_variableAddresses;
|
||||
int m_currentStackOffset;
|
||||
|
||||
std::string GenerateUniqueLabel(const std::string& prefix) {
|
||||
return prefix + "_" + std::to_string(m_labelCounter++);
|
||||
}
|
||||
|
||||
void GenerateNodeCode(std::shared_ptr<ASTNode> node) {
|
||||
if (!node) return;
|
||||
|
||||
if (node->IsType<FunctionEntryNode>()) {
|
||||
// Generate code for function entry
|
||||
m_assembly << " ; Function Entry\n";
|
||||
for (auto& child : node->children) {
|
||||
GenerateNodeCode(child);
|
||||
}
|
||||
}
|
||||
else if (node->IsType<BranchNode>()) {
|
||||
GenerateBranchCode(node);
|
||||
}
|
||||
else if (node->IsType<PrintNode>()) {
|
||||
GeneratePrintCode(node);
|
||||
}
|
||||
else if (node->IsType<VariableNode>()) {
|
||||
GenerateVariableCode(node);
|
||||
}
|
||||
else if (node->IsType<OperatorNode>()) {
|
||||
GenerateOperatorCode(node);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateBranchCode(std::shared_ptr<ASTNode> node) {
|
||||
std::string labelTrue = GenerateUniqueLabel("true");
|
||||
std::string labelFalse = GenerateUniqueLabel("false");
|
||||
std::string labelEnd = GenerateUniqueLabel("end");
|
||||
|
||||
m_assembly << " ; Branch condition evaluation\n";
|
||||
|
||||
// Generate condition code
|
||||
if (auto conditionNode = node->GetDataInput(0)) {
|
||||
GenerateNodeCode(conditionNode);
|
||||
}
|
||||
|
||||
m_assembly << " pop eax\n";
|
||||
m_assembly << " cmp eax, 0\n";
|
||||
m_assembly << " je " << labelFalse << "\n";
|
||||
|
||||
// True branch
|
||||
m_assembly << labelTrue << ":\n";
|
||||
if (node->GetChildCount() > 0) {
|
||||
GenerateNodeCode(node->GetChild(0));
|
||||
}
|
||||
m_assembly << " jmp " << labelEnd << "\n";
|
||||
|
||||
// False branch
|
||||
m_assembly << labelFalse << ":\n";
|
||||
if (node->GetChildCount() > 1) {
|
||||
GenerateNodeCode(node->GetChild(1));
|
||||
}
|
||||
|
||||
m_assembly << labelEnd << ":\n";
|
||||
}
|
||||
|
||||
void GeneratePrintCode(std::shared_ptr<ASTNode> node) {
|
||||
auto* printNode = node->GetAs<PrintNode>();
|
||||
if (!printNode) return;
|
||||
|
||||
m_assembly << " ; Print message\n";
|
||||
if (printNode->GetText() == "Good!") {
|
||||
m_assembly << " mov edx, 6\n"; // message length
|
||||
m_assembly << " mov ecx, msg_good\n";
|
||||
} else {
|
||||
m_assembly << " mov edx, 6\n"; // message length
|
||||
m_assembly << " mov ecx, msg_bad\n";
|
||||
}
|
||||
m_assembly << " mov ebx, 1\n"; // file descriptor (stdout)
|
||||
m_assembly << " mov eax, 4\n"; // sys_write
|
||||
m_assembly << " int 0x80\n";
|
||||
}
|
||||
|
||||
void GenerateVariableCode(std::shared_ptr<ASTNode> node) {
|
||||
auto* varNode = node->GetAs<VariableNode>();
|
||||
if (!varNode) return;
|
||||
|
||||
// For this example, we'll just push a value onto the stack
|
||||
std::string varName = varNode->GetVariableName();
|
||||
if (m_variableAddresses.find(varName) == m_variableAddresses.end()) {
|
||||
m_variableAddresses[varName] = m_currentStackOffset;
|
||||
m_currentStackOffset += 4;
|
||||
}
|
||||
|
||||
m_assembly << " ; Load variable " << varName << "\n";
|
||||
m_assembly << " mov eax, [ebp-" << m_variableAddresses[varName] << "]\n";
|
||||
m_assembly << " push eax\n";
|
||||
}
|
||||
|
||||
void GenerateOperatorCode(std::shared_ptr<ASTNode> node) {
|
||||
auto* opNode = node->GetAs<OperatorNode>();
|
||||
if (!opNode) return;
|
||||
|
||||
// Generate code for operands
|
||||
for (const auto& [port, inputNode] : node->dataInputs) {
|
||||
GenerateNodeCode(inputNode);
|
||||
}
|
||||
|
||||
m_assembly << " ; Operator " << static_cast<int>(opNode->GetOperationType()) << "\n";
|
||||
|
||||
switch (opNode->GetOperationType()) {
|
||||
case OperatorNode::ADD:
|
||||
m_assembly << " pop ebx\n";
|
||||
m_assembly << " pop eax\n";
|
||||
m_assembly << " add eax, ebx\n";
|
||||
m_assembly << " push eax\n";
|
||||
break;
|
||||
case OperatorNode::SUBTRACT:
|
||||
m_assembly << " pop ebx\n";
|
||||
m_assembly << " pop eax\n";
|
||||
m_assembly << " sub eax, ebx\n";
|
||||
m_assembly << " push eax\n";
|
||||
break;
|
||||
// Add other operators...
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
*/
|
||||
/*
|
||||
|
||||
// Générer des labels uniques pour les nœuds
|
||||
for (const auto& nodePair : m_ast.nodeMap) {
|
||||
std::string label = m_ast.generateLabel(m_ast);
|
||||
labels[nodePair.first] = label;
|
||||
|
||||
// Générer du code pour chaque type de nœud
|
||||
std::string nodeType = nodePair.second->node->GetTypeName();
|
||||
if (nodeType == "LOOP") {
|
||||
// Générer du code pour une boucle
|
||||
assemblyCode += " ; Loop start\n";
|
||||
// Ajouter des instructions pour la condition de la boucle
|
||||
if (nodePair.second->condition) {
|
||||
assemblyCode += " cmp_eq r0, " + labels[nodePair.second->condition->node->GetId()] + ", 0\n";
|
||||
assemblyCode += " skipz r0\n";
|
||||
assemblyCode += " jump " + labels[nodePair.second->body.front()->node->GetId()] + "\n";
|
||||
}
|
||||
// Ajouter des instructions pour le corps de la boucle
|
||||
for (const auto& bodyNode : nodePair.second->body) {
|
||||
assemblyCode += " ; Loop body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps
|
||||
}
|
||||
assemblyCode += " jump " + label + "\n"; // Retour au début de la boucle
|
||||
} else if (nodeType == "BRANCH") {
|
||||
// Générer du code pour un branchement
|
||||
assemblyCode += " ; Branch start\n";
|
||||
// Ajouter des instructions pour la condition du branchement
|
||||
if (nodePair.second->condition) {
|
||||
assemblyCode += " cmp_eq r0, " + labels[nodePair.second->condition->node->GetId()] + ", 0\n";
|
||||
assemblyCode += " skipnz r0\n";
|
||||
assemblyCode += " jump " + labels[nodePair.second->body.front()->node->GetId()] + "\n";
|
||||
}
|
||||
// Ajouter des instructions pour le corps du branchement
|
||||
for (const auto& bodyNode : nodePair.second->body) {
|
||||
assemblyCode += " ; Branch body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps
|
||||
}
|
||||
// Ajouter des instructions pour le corps du else
|
||||
if (nodePair.second->elseBody) {
|
||||
assemblyCode += " ; Else body\n";
|
||||
// Ajouter des instructions pour chaque nœud du corps du else
|
||||
}
|
||||
} else {
|
||||
// Générer du code pour d'autres types de nœuds
|
||||
assemblyCode += " ; Other node type: " + nodeType + "\n";
|
||||
assemblyCode += nodePair.second->node->GenerateAssembly();
|
||||
|
||||
// Ajouter des instructions spécifiques au type de nœud
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ void PrintNode::Initialize()
|
|||
|
||||
void PrintNode::SetText(const std::string &text)
|
||||
{
|
||||
m_variables.at(m_label)->SetValue<std::string>(text);
|
||||
m_variables.at(m_label)->SetTextValue(text);
|
||||
SetInternalData({{"text", text}});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(compiler_test LANGUAGES CXX C)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
main.cpp
|
||||
test_ast.cpp
|
||||
|
||||
|
||||
../src/nodes/base_node.cpp
|
||||
../src/nodes/branch_node.cpp
|
||||
../src/nodes/print_node.cpp
|
||||
../src/nodes/variable_node.cpp
|
||||
../src/nodes/syscall_node.cpp
|
||||
../src/nodes/connection.cpp
|
||||
|
||||
../../chip32/chip32_assembler.cpp
|
||||
../../chip32/chip32_vm.c
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
../../chip32
|
||||
../src
|
||||
../interfaces
|
||||
../lib
|
||||
../../../shared
|
||||
../src/nodes
|
||||
../src/compiler
|
||||
)
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2022 Anthony Rabine
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
||||
|
||||
void hexdump(void *ptr, int buflen) {
|
||||
unsigned char *buf = (unsigned char*)ptr;
|
||||
int i, j;
|
||||
for (i=0; i<buflen; i+=16) {
|
||||
printf("%06x: ", i);
|
||||
for (j=0; j<16; j++)
|
||||
if (i+j < buflen)
|
||||
printf("%02x ", buf[i+j]);
|
||||
else
|
||||
printf(" ");
|
||||
printf(" ");
|
||||
for (j=0; j<16; j++)
|
||||
if (i+j < buflen)
|
||||
printf("%c", isprint(buf[i+j]) ? buf[i+j] : '.');
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
35
core/tests/CMakeLists.txt
Normal file
35
core/tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
project(core_tests LANGUAGES CXX C)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
main.cpp
|
||||
test_parser.cpp
|
||||
test_vm.cpp
|
||||
test_ast.cpp
|
||||
test_print_node.cpp
|
||||
|
||||
|
||||
../story-manager/src/nodes/base_node.cpp
|
||||
../story-manager/src/nodes/branch_node.cpp
|
||||
../story-manager/src/nodes/print_node.cpp
|
||||
../story-manager/src/nodes/variable_node.cpp
|
||||
../story-manager/src/nodes/syscall_node.cpp
|
||||
../story-manager/src/nodes/connection.cpp
|
||||
|
||||
../chip32/chip32_assembler.cpp
|
||||
../chip32/chip32_vm.c
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
../chip32
|
||||
../story-manager/src
|
||||
../story-manager/src/nodes
|
||||
../story-manager/src/compiler
|
||||
../story-manager/interfaces
|
||||
../../shared
|
||||
|
||||
)
|
||||
|
|
@ -10677,7 +10677,7 @@ namespace Catch {
|
|||
|
||||
// end catch_exception_translator_registry.h
|
||||
#ifdef __OBJC__
|
||||
#import "Foundation/Foundation.h"
|
||||
#import "../story-manager/tests/Foundation/Foundation.h"
|
||||
#endif
|
||||
|
||||
namespace Catch {
|
||||
|
|
@ -45,7 +45,7 @@ THE SOFTWARE.
|
|||
#include "flow_generator.h"
|
||||
#include "assembly_generator_chip32.h"
|
||||
|
||||
TEST_CASE( "Check various indentations and typos" ) {
|
||||
TEST_CASE( "Check AST with basic nodes" ) {
|
||||
|
||||
Compiler compiler;
|
||||
|
||||
|
|
@ -215,6 +215,7 @@ TEST_CASE( "Check various indentations and typos" ) {
|
|||
|
||||
// Create generator context with current time and user
|
||||
AssemblyGenerator::GeneratorContext context(
|
||||
variables,
|
||||
"2025-04-08 12:09:01", // Current UTC time
|
||||
"unit-test-ast", // Current user
|
||||
true, // Enable debug output
|
||||
|
|
@ -248,7 +249,7 @@ TEST_CASE( "Check various indentations and typos" ) {
|
|||
// Generate data section
|
||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||
generator.GenerateNodesVariables(nodes);
|
||||
generator.GenerateGlobalVariables(variables);
|
||||
generator.GenerateGlobalVariables();
|
||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||
generator.GenerateTextSection(pathTree);
|
||||
generator.GenerateExit();
|
||||
|
|
@ -35,15 +35,7 @@ Purpose: grammar, ram usage and macros, rom code generation
|
|||
|
||||
void hexdump(void *ptr, int buflen);
|
||||
|
||||
static const std::string test1 = R"(; jump over the data, to our entry label
|
||||
|
||||
$imageBird DC8 "example.bmp", 8 ; data
|
||||
$someConstant DC32 12456789
|
||||
|
||||
; DSxx to declare a variable in RAM, followed by the number of elements
|
||||
$RamData1 DV32 1 ; one 32-bit integer
|
||||
$MyArray DV8 10 ; array of 10 bytes
|
||||
|
||||
static const std::string test1 = R"(
|
||||
; label definition
|
||||
.main: ;; comment here should work
|
||||
; We create a stupid loop just for RAM variable testing
|
||||
|
|
@ -64,6 +56,14 @@ $MyArray DV8 10 ; array of 10 bytes
|
|||
mov R0,R2 ; copy R2 into R0 (NO blank space between , and R2)
|
||||
|
||||
halt
|
||||
|
||||
$imageBird DC8 "example.bmp", 8 ; data
|
||||
$someConstant DC32 12456789
|
||||
|
||||
; DSxx to declare a variable in RAM, followed by the number of elements
|
||||
$RamData1 DV32 1 ; one 32-bit integer
|
||||
$MyArray DV8 10 ; array of 10 bytes
|
||||
|
||||
)";
|
||||
|
||||
#include <stdarg.h>
|
||||
239
core/tests/test_print_node.cpp
Normal file
239
core/tests/test_print_node.cpp
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
// test_print_with_args.cpp
|
||||
// Test unitaire pour vérifier que les arguments du Print sont bien pris en compte
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "print_node.h"
|
||||
#include "variable_node.h"
|
||||
#include "function_entry_node.h"
|
||||
#include "operator_node.h"
|
||||
#include "connection.h"
|
||||
#include "ast_builder.h"
|
||||
#include "assembly_generator_chip32.h"
|
||||
#include "chip32_machine.h"
|
||||
#include "variable.h"
|
||||
|
||||
TEST_CASE("Print with single argument") {
|
||||
// Create the print node
|
||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||
printNode->SetText("Compteur: %d");
|
||||
|
||||
// Create function entry
|
||||
auto functionEntryNode = std::make_shared<FunctionEntryNode>("function-entry-node");
|
||||
|
||||
// IMPORTANT: Create the "counter" variable and add it to the global variables list
|
||||
std::vector<std::shared_ptr<Variable>> variables;
|
||||
auto counterVar = std::make_shared<Variable>("counter");
|
||||
counterVar->SetIntegerValue(42); // Initial value
|
||||
variables.push_back(counterVar); // ← CRUCIAL: Add to global variables
|
||||
|
||||
// Create a variable node that references the counter variable
|
||||
auto variableNodeCounter = std::make_shared<VariableNode>("variable-node");
|
||||
variableNodeCounter->SetVariable(counterVar);
|
||||
|
||||
// Build the node list
|
||||
std::vector<std::shared_ptr<BaseNode>> nodes;
|
||||
nodes.push_back(functionEntryNode);
|
||||
nodes.push_back(printNode);
|
||||
nodes.push_back(variableNodeCounter);
|
||||
|
||||
// Create connections
|
||||
std::vector<std::shared_ptr<Connection>> connections;
|
||||
|
||||
// Connect function entry to print node (execution flow)
|
||||
auto cn1 = std::make_shared<Connection>();
|
||||
cn1->inNodeId = printNode->GetId();
|
||||
cn1->inPortIndex = 0;
|
||||
cn1->outNodeId = functionEntryNode->GetId();
|
||||
cn1->outPortIndex = 0;
|
||||
cn1->type = Connection::EXECUTION_LINK;
|
||||
connections.push_back(cn1);
|
||||
|
||||
// Connect variable node to print node (data flow - arg0)
|
||||
auto cn2 = std::make_shared<Connection>();
|
||||
cn2->inNodeId = printNode->GetId();
|
||||
cn2->inPortIndex = 1; // arg0 input port
|
||||
cn2->outNodeId = variableNodeCounter->GetId();
|
||||
cn2->outPortIndex = 0;
|
||||
cn2->type = Connection::DATA_LINK;
|
||||
connections.push_back(cn2);
|
||||
|
||||
// Create generator context with the variables list
|
||||
AssemblyGenerator::GeneratorContext context(
|
||||
variables, // ← IMPORTANT: Pass the variables including counter
|
||||
"2025-01-10 10:00:00",
|
||||
"test-print-args",
|
||||
true,
|
||||
true,
|
||||
1024
|
||||
);
|
||||
|
||||
// Create generator
|
||||
AssemblyGeneratorChip32 generator(context);
|
||||
|
||||
// Build AST
|
||||
ASTBuilder builder(nodes, connections);
|
||||
auto pathTree = builder.BuildAST();
|
||||
|
||||
// Generate assembly
|
||||
generator.Reset();
|
||||
generator.GenerateHeader();
|
||||
|
||||
// DATA section - this will now include the counter variable
|
||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||
generator.GenerateNodesVariables(nodes); // Print node format string
|
||||
generator.GenerateGlobalVariables(); // ← This generates counter variable
|
||||
|
||||
// TEXT section
|
||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||
generator.GenerateTextSection(pathTree);
|
||||
generator.GenerateExit();
|
||||
|
||||
std::string assembly = generator.GetAssembly();
|
||||
|
||||
std::cout << "===== Generated Assembly =====" << std::endl;
|
||||
std::cout << assembly << std::endl;
|
||||
|
||||
// Now the assembly should include the counter variable declaration:
|
||||
// $XEIxIsZoXA DV32, 42 ; counter
|
||||
|
||||
Chip32::Machine machine;
|
||||
machine.QuickExecute(assembly);
|
||||
}
|
||||
|
||||
TEST_CASE("Print with multiple arguments") {
|
||||
// ===== Setup =====
|
||||
|
||||
// Variables
|
||||
auto var_a = std::make_shared<Variable>("a");
|
||||
var_a->SetIntegerValue(10); // ← CORRECTION: Utiliser SetIntegerValue() au lieu de SetValue<int>()
|
||||
|
||||
auto var_b = std::make_shared<Variable>("b");
|
||||
var_b->SetIntegerValue(20); // ← CORRECTION: Utiliser SetIntegerValue() au lieu de SetValue<int>()
|
||||
|
||||
std::vector<std::shared_ptr<Variable>> variables = {var_a, var_b};
|
||||
|
||||
// Nœuds
|
||||
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry-node");
|
||||
functionEntry->SetWeight(100);
|
||||
|
||||
auto varNodeA = std::make_shared<VariableNode>("variable-node");
|
||||
varNodeA->SetVariableUuid(var_a->GetUuid());
|
||||
|
||||
auto varNodeB = std::make_shared<VariableNode>("variable-node");
|
||||
varNodeB->SetVariableUuid(var_b->GetUuid());
|
||||
|
||||
auto addNode = std::make_shared<OperatorNode>("operator-node");
|
||||
addNode->SetOperationType(OperatorNode::OperationType::ADD);
|
||||
|
||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||
printNode->SetText("Calcul: %d + %d = %d");
|
||||
// IMPORTANT: Appeler Initialize() si nécessaire après SetText()
|
||||
// printNode->Initialize(); // Si le test ne charge pas depuis JSON
|
||||
|
||||
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||
functionEntry,
|
||||
varNodeA,
|
||||
varNodeB,
|
||||
addNode,
|
||||
printNode
|
||||
};
|
||||
|
||||
// Connexions
|
||||
std::vector<std::shared_ptr<Connection>> connections;
|
||||
|
||||
// Execution: Entry → Print
|
||||
auto execConn = std::make_shared<Connection>();
|
||||
execConn->outNodeId = functionEntry->GetId();
|
||||
execConn->outPortIndex = 0;
|
||||
execConn->inNodeId = printNode->GetId();
|
||||
execConn->inPortIndex = 0;
|
||||
execConn->type = Connection::EXECUTION_LINK;
|
||||
connections.push_back(execConn);
|
||||
|
||||
// Data: varA → Print.arg0
|
||||
auto dataConn1 = std::make_shared<Connection>();
|
||||
dataConn1->outNodeId = varNodeA->GetId();
|
||||
dataConn1->outPortIndex = 0;
|
||||
dataConn1->inNodeId = printNode->GetId();
|
||||
dataConn1->inPortIndex = 1; // ← CORRECTION: arg0 = port 1 (port 0 = execution)
|
||||
dataConn1->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn1);
|
||||
|
||||
// Data: varB → Print.arg1
|
||||
auto dataConn2 = std::make_shared<Connection>();
|
||||
dataConn2->outNodeId = varNodeB->GetId();
|
||||
dataConn2->outPortIndex = 0;
|
||||
dataConn2->inNodeId = printNode->GetId();
|
||||
dataConn2->inPortIndex = 2; // ← CORRECTION: arg1 = port 2
|
||||
dataConn2->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn2);
|
||||
|
||||
// Data: varA → ADD.input0
|
||||
auto dataConn3 = std::make_shared<Connection>();
|
||||
dataConn3->outNodeId = varNodeA->GetId();
|
||||
dataConn3->outPortIndex = 0;
|
||||
dataConn3->inNodeId = addNode->GetId();
|
||||
dataConn3->inPortIndex = 0;
|
||||
dataConn3->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn3);
|
||||
|
||||
// Data: varB → ADD.input1
|
||||
auto dataConn4 = std::make_shared<Connection>();
|
||||
dataConn4->outNodeId = varNodeB->GetId();
|
||||
dataConn4->outPortIndex = 0;
|
||||
dataConn4->inNodeId = addNode->GetId();
|
||||
dataConn4->inPortIndex = 1;
|
||||
dataConn4->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn4);
|
||||
|
||||
// Data: ADD → Print.arg2
|
||||
auto dataConn5 = std::make_shared<Connection>();
|
||||
dataConn5->outNodeId = addNode->GetId();
|
||||
dataConn5->outPortIndex = 0;
|
||||
dataConn5->inNodeId = printNode->GetId();
|
||||
dataConn5->inPortIndex = 3; // ← CORRECTION: arg2 = port 3
|
||||
dataConn5->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn5);
|
||||
|
||||
// ===== Build & Generate =====
|
||||
|
||||
ASTBuilder builder(nodes, connections);
|
||||
auto pathTree = builder.BuildAST();
|
||||
|
||||
AssemblyGenerator::GeneratorContext context(
|
||||
variables,
|
||||
"2025-01-10 10:00:00",
|
||||
"test-print-multi-args",
|
||||
true, true, 1024
|
||||
);
|
||||
|
||||
AssemblyGeneratorChip32 generator(context);
|
||||
generator.Reset();
|
||||
generator.GenerateHeader();
|
||||
|
||||
|
||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||
generator.GenerateNodesVariables(nodes);
|
||||
generator.GenerateGlobalVariables();
|
||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||
generator.GenerateTextSection(pathTree);
|
||||
generator.GenerateExit();
|
||||
|
||||
std::string assembly = generator.GetAssembly();
|
||||
|
||||
std::cout << "\n===== Generated Assembly (Multi-Args) =====\n" << assembly << std::endl;
|
||||
|
||||
// ===== Vérifications =====
|
||||
|
||||
// // Vérifier 3 arguments
|
||||
// REQUIRE(assembly.find("lcons r1, 3") != std::string::npos);
|
||||
|
||||
// // Vérifier chargement des 3 registres
|
||||
// REQUIRE(assembly.find("load r2") != std::string::npos); // arg0
|
||||
// REQUIRE(assembly.find("load r3") != std::string::npos); // arg1
|
||||
// // r4 vient de l'opérateur ADD (devrait être sur la pile ou dans r4)
|
||||
|
||||
// Execute
|
||||
Chip32::Machine machine;
|
||||
machine.QuickExecute(assembly);
|
||||
}
|
||||
Loading…
Reference in a new issue