merged tests all in core now, added print test
Some checks failed
Build-StoryEditor / build_linux (push) Has been cancelled
Build-StoryEditor / build_win32 (push) Has been cancelled
Deploy-Documentation / deploy (push) Has been cancelled

This commit is contained in:
anthony@rabine.fr 2025-10-04 10:51:14 +02:00
parent 88b9bc6b3e
commit 9c6df3c9b6
17 changed files with 493 additions and 18482 deletions

2
.gitignore vendored
View file

@ -85,5 +85,5 @@ story-editor/.flatpak-builder
story-editor/flatpak_build/ story-editor/flatpak_build/
story-editor/AppDir/ story-editor/AppDir/
core/story-manager/tests/build
story-player/android/build/reports/problems/problems-report.html story-player/android/build/reports/problems/problems-report.html
core/tests/build

34
.vscode/launch.json vendored
View file

@ -69,41 +69,13 @@
}, },
{ {
"name": "Debug VM Tests", "name": "Debug tests",
"type": "cppdbg", "type": "cppdbg",
"request": "launch", "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": [], "args": [],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}/core/chip32/tests", "cwd": "${workspaceFolder}/core/tests/build",
"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",
"environment": [], "environment": [],
"externalConsole": false, "externalConsole": false,
"linux": { "linux": {

View file

@ -1,8 +1,7 @@
{ {
"cmake.sourceDirectory": [ "cmake.sourceDirectory": [
"${workspaceFolder}/core/chip32/tests", "${workspaceFolder}/core/tests",
"${workspaceFolder}/core/story-manager/tests",
"${workspaceFolder}/story-editor", "${workspaceFolder}/story-editor",
"${workspaceFolder}/story-player-raylib", "${workspaceFolder}/story-player-raylib",
"${workspaceFolder}/software" "${workspaceFolder}/software"

View file

@ -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})

View file

@ -3,6 +3,7 @@
#include "ast_builder.h" #include "ast_builder.h"
#include "assembly_generator.h" #include "assembly_generator.h"
#include "call_function_node.h" #include "call_function_node.h"
#include <algorithm>
class AssemblyGeneratorChip32 : public AssemblyGenerator class AssemblyGeneratorChip32 : public AssemblyGenerator
{ {
@ -137,19 +138,97 @@ private:
m_depth--; m_depth--;
} }
void GeneratePrintNode(std::shared_ptr<ASTNode> node) { void GeneratePrintNode(std::shared_ptr<ASTNode> node)
{
auto* printNode = node->GetAs<PrintNode>(); auto* printNode = node->GetAs<PrintNode>();
if (!printNode) return; if (!printNode) return;
std::string label = printNode->GetLabel(); 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" m_assembly << " push r0\n"
<< " push r1\n" << " push r1\n";
<< " lcons r0, $" << label << "\n"
<< " lcons r1, 0 ; number of arguments\n" // FIXME: handle arguments // Save argument registers if we have arguments
<< " syscall 4\n" if (argCount > 0) m_assembly << " push r2\n";
<< " pop r1\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"; << " pop r0\n";
m_depth--;
} }
void GenerateOperatorNode(std::shared_ptr<ASTNode> node) { void GenerateOperatorNode(std::shared_ptr<ASTNode> node) {
@ -175,157 +254,183 @@ private:
if (var) if (var)
{ {
// Generate code to load the variable value // Generate code to load the variable value
// FIXME: hardcoded 4 bytes, replace by actual real variable size int varSize = GetVariableSize(var);
m_assembly << " load r" << reg << ", $" << var->GetLabel() << ", 4" << " ; Load variable " << var->GetVariableName() << "\n"; m_assembly << " load r" << reg << ", $" << var->GetLabel()
<< ", " << varSize << " ; Load variable "
<< var->GetVariableName() << "\n";
m_assembly << " push r" << reg << "\n"; m_assembly << " push r" << reg << "\n";
} }
else 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 // Generate operator code based on type
switch (opNode->GetOperationType()) { switch (opNode->GetOperationType()) {
// ===== ARITHMETIC OPERATORS =====
case OperatorNode::OperationType::ADD: case OperatorNode::OperationType::ADD:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " add r0, r1\n" << " add r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::SUBTRACT: case OperatorNode::OperationType::SUBTRACT:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " sub r0, r1\n" << " sub r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::MULTIPLY: case OperatorNode::OperationType::MULTIPLY:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " mul r0, r1\n" << " mul r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::DIVIDE: case OperatorNode::OperationType::DIVIDE:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " div r0, r1\n" << " div r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::MODULO:
m_assembly << " pop r0\n" // ===== COMPARISON OPERATORS =====
<< " pop r1\n" // Utilise les instructions eq, gt, lt de Chip32
<< " mod r0, r1\n" // Syntaxe: eq r_dest, r_op1, r_op2 → r_dest = (r_op1 == r_op2 ? 1 : 0)
<< " push r0\n";
break;
case OperatorNode::OperationType::EQUAL: case OperatorNode::OperationType::EQUAL:
m_assembly << " pop r0\n" m_assembly << " pop r1 ; second operand\n"
<< " pop r1\n" << " pop r0 ; first operand\n"
<< " cmp r0, r1\n" << " eq r0, r0, r1 ; r0 = (r0 == r1 ? 1 : 0)\n"
<< " lcons r0, 1\n"
<< " skipz\n"
<< " lcons r0, 0\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::NOT_EQUAL: case OperatorNode::OperationType::NOT_EQUAL:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " cmp r0, r1\n" << " eq r0, r0, r1 ; r0 = (r0 == r1 ? 1 : 0)\n"
<< " lcons r0, 0\n" << " lcons r2, 1\n"
<< " skipz\n" << " xor r0, r2 ; inverse: 0→1, 1→0\n"
<< " lcons r0, 1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::GREATER_THAN: case OperatorNode::OperationType::GREATER_THAN:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " cmp r0, r1\n" << " gt r0, r0, r1 ; r0 = (r0 > r1 ? 1 : 0)\n"
<< " lcons r0, 1\n"
<< " skipgt\n"
<< " lcons r0, 0\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::LESS_THAN: case OperatorNode::OperationType::LESS_THAN:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " cmp r0, r1\n" << " lt r0, r0, r1 ; r0 = (r0 < r1 ? 1 : 0)\n"
<< " lcons r0, 1\n"
<< " skiplt\n"
<< " lcons r0, 0\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::GREATER_EQUAL: case OperatorNode::OperationType::GREATER_EQUAL:
m_assembly << " pop r0\n" // >= est équivalent à NOT(<)
<< " pop r1\n" m_assembly << " pop r1\n"
<< " cmp r0, r1\n" << " pop r0\n"
<< " lcons r0, 1\n" << " lt r0, r0, r1 ; r0 = (r0 < r1 ? 1 : 0)\n"
<< " skipge\n" << " lcons r2, 1\n"
<< " lcons r0, 0\n" << " xor r0, r2 ; inverse: >= est NOT(<)\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::LESS_EQUAL: case OperatorNode::OperationType::LESS_EQUAL:
m_assembly << " pop r0\n" // <= est équivalent à NOT(>)
<< " pop r1\n" m_assembly << " pop r1\n"
<< " cmp r0, r1\n" << " pop r0\n"
<< " lcons r0, 1\n" << " gt r0, r0, r1 ; r0 = (r0 > r1 ? 1 : 0)\n"
<< " skiple\n" << " lcons r2, 1\n"
<< " lcons r0, 0\n" << " xor r0, r2 ; inverse: <= est NOT(>)\n"
<< " push r0\n"; << " push r0\n";
break; break;
// ===== LOGICAL OPERATORS =====
case OperatorNode::OperationType::AND: case OperatorNode::OperationType::AND:
m_assembly << " pop r0\n" // AND logique: résultat 1 si les deux sont non-zéro
<< " pop r1\n" m_assembly << " pop r1\n"
<< " and r0, 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"; << " push r0\n";
break; break;
case OperatorNode::OperationType::OR: case OperatorNode::OperationType::OR:
m_assembly << " pop r0\n" // OR logique: résultat 1 si au moins un est non-zéro
<< " pop r1\n" m_assembly << " pop r1\n"
<< " or r0, 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"; << " push r0\n";
break; break;
case OperatorNode::OperationType::NOT: case OperatorNode::OperationType::NOT:
// NOT logique: 0→1, non-zero→0
m_assembly << " pop r0\n" 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"; << " push r0\n";
break; break;
// ===== BITWISE OPERATORS =====
case OperatorNode::OperationType::BITWISE_AND: case OperatorNode::OperationType::BITWISE_AND:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " and r0, r1\n" << " and r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::BITWISE_OR: case OperatorNode::OperationType::BITWISE_OR:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " or r0, r1\n" << " or r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::BITWISE_XOR: case OperatorNode::OperationType::BITWISE_XOR:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " xor r0, r1\n" << " xor r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::BITWISE_NOT: case OperatorNode::OperationType::BITWISE_NOT:
m_assembly << " pop r0\n" m_assembly << " pop r0\n"
<< " not r0\n" << " not r0\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::LEFT_SHIFT: case OperatorNode::OperationType::LEFT_SHIFT:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " shl r0, r1\n" << " shl r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
case OperatorNode::OperationType::RIGHT_SHIFT: case OperatorNode::OperationType::RIGHT_SHIFT:
m_assembly << " pop r0\n" m_assembly << " pop r1\n"
<< " pop r1\n" << " pop r0\n"
<< " shr r0, r1\n" << " shr r0, r1\n"
<< " push r0\n"; << " push r0\n";
break; break;
default: default:
throw std::runtime_error("Unsupported operator type"); 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"; 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");
}
}
}; };

View file

@ -85,9 +85,6 @@ public:
} }
} }
void printAST(const AST& ast, const std::shared_ptr<AstNode>& node, const std::string& prefix = "", bool isLast = true) { void printAST(const AST& ast, const std::shared_ptr<AstNode>& node, const std::string& prefix = "", bool isLast = true) {
// Afficher le nœud actuel // Afficher le nœud actuel
std::cout << prefix; std::cout << prefix;
@ -141,68 +138,6 @@ public:
printAST(m_ast, entryNode); 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 { std::string GetCode() const {
return m_ast.assemblyCode; return m_ast.assemblyCode;
} }
@ -211,232 +146,3 @@ private:
AST m_ast; 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
}
}
*/

View file

@ -32,7 +32,7 @@ void PrintNode::Initialize()
void PrintNode::SetText(const std::string &text) 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}}); SetInternalData({{"text", text}});
} }

View file

@ -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

View file

@ -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
View 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
)

View file

@ -10677,7 +10677,7 @@ namespace Catch {
// end catch_exception_translator_registry.h // end catch_exception_translator_registry.h
#ifdef __OBJC__ #ifdef __OBJC__
#import "Foundation/Foundation.h" #import "../story-manager/tests/Foundation/Foundation.h"
#endif #endif
namespace Catch { namespace Catch {

View file

@ -45,7 +45,7 @@ THE SOFTWARE.
#include "flow_generator.h" #include "flow_generator.h"
#include "assembly_generator_chip32.h" #include "assembly_generator_chip32.h"
TEST_CASE( "Check various indentations and typos" ) { TEST_CASE( "Check AST with basic nodes" ) {
Compiler compiler; Compiler compiler;
@ -215,6 +215,7 @@ TEST_CASE( "Check various indentations and typos" ) {
// Create generator context with current time and user // Create generator context with current time and user
AssemblyGenerator::GeneratorContext context( AssemblyGenerator::GeneratorContext context(
variables,
"2025-04-08 12:09:01", // Current UTC time "2025-04-08 12:09:01", // Current UTC time
"unit-test-ast", // Current user "unit-test-ast", // Current user
true, // Enable debug output true, // Enable debug output
@ -248,7 +249,7 @@ TEST_CASE( "Check various indentations and typos" ) {
// Generate data section // Generate data section
generator.StartSection(AssemblyGenerator::Section::DATA); generator.StartSection(AssemblyGenerator::Section::DATA);
generator.GenerateNodesVariables(nodes); generator.GenerateNodesVariables(nodes);
generator.GenerateGlobalVariables(variables); generator.GenerateGlobalVariables();
generator.StartSection(AssemblyGenerator::Section::TEXT); generator.StartSection(AssemblyGenerator::Section::TEXT);
generator.GenerateTextSection(pathTree); generator.GenerateTextSection(pathTree);
generator.GenerateExit(); generator.GenerateExit();

View file

@ -35,15 +35,7 @@ Purpose: grammar, ram usage and macros, rom code generation
void hexdump(void *ptr, int buflen); void hexdump(void *ptr, int buflen);
static const std::string test1 = R"(; jump over the data, to our entry label static const std::string test1 = R"(
$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
; label definition ; label definition
.main: ;; comment here should work .main: ;; comment here should work
; We create a stupid loop just for RAM variable testing ; 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) mov R0,R2 ; copy R2 into R0 (NO blank space between , and R2)
halt 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> #include <stdarg.h>

View 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);
}