Add TAC in compile pipeline
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.fr 2025-10-06 10:24:25 +02:00
parent b76add6793
commit eb08627029
17 changed files with 2807 additions and 952 deletions

View file

@ -365,7 +365,7 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
{ {
if (i.isRamData) if (i.isRamData)
{ {
result.ramUsageSize += i.dataLen * i.dataTypeSize/8; result.ramUsageSize += i.dataLen;
} }
else else
{ {
@ -373,7 +373,10 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
{ {
program.push_back(i.code.opcode); program.push_back(i.code.opcode);
} }
result.constantsSize += i.compiledArgs.size(); else if (i.isRomData) // Seulement pour ROM DATA
{
result.constantsSize += i.compiledArgs.size();
}
std::copy (i.compiledArgs.begin(), i.compiledArgs.end(), std::back_inserter(program)); std::copy (i.compiledArgs.begin(), i.compiledArgs.end(), std::back_inserter(program));
} }
} }

View file

@ -10,6 +10,8 @@
#include "chip32_assembler.h" #include "chip32_assembler.h"
#include "chip32_macros.h" #include "chip32_macros.h"
// Dans chip32_machine.h
namespace Chip32 namespace Chip32
{ {
@ -19,119 +21,207 @@ public:
bool parseResult{false}; bool parseResult{false};
bool buildResult{false}; bool buildResult{false};
chip32_result_t runResult{VM_OK}; chip32_result_t runResult{VM_OK};
std::string printOutput;
static int get_from_memory(chip32_ctx_t *ctx, uint32_t addr, char *text) static Machine *m_instance;
{
int valid = 0;
// Test if address is valid Machine() {
// Bind syscall handler to this instance
bool isRam = addr & 0x80000000; m_syscallHandler = std::bind(&Machine::HandleSyscall, this,
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing std::placeholders::_1,
if (isRam) { std::placeholders::_2);
strcpy(&text[0], (const char *)&ctx->ram.mem[addr]);
} else {
strcpy(&text[0], (const char *)&ctx->rom.mem[addr]);
}
return valid;
} }
// Lecture d'une chaîne depuis la mémoire (non statique maintenant)
std::string GetStringFromMemory(chip32_ctx_t *ctx, uint32_t addr)
static uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code)
{ {
uint8_t retCode = SYSCALL_RET_OK; if (!ctx) {
char working_buf[100] = {0}; throw std::runtime_error("Invalid context in GetStringFromMemory");
}
// Printf bool isRam = (addr & 0x80000000) != 0;
if (code == 4) addr &= 0xFFFF;
{
// In R0: string with escaped characters
// R1: Number of arguments
// R2, R3 ... arguments
// Integers: stored in registers by values const uint8_t* source_mem = nullptr;
// Strings: first character address in register size_t mem_size = 0;
get_from_memory(ctx, ctx->registers[R0], working_buf); if (isRam) {
int arg_count = ctx->registers[R1]; if (addr >= ctx->ram.size) {
throw std::out_of_range("RAM address out of bounds: " + std::to_string(addr));
}
source_mem = ctx->ram.mem;
mem_size = ctx->ram.size;
} else {
if (addr >= ctx->rom.size) {
throw std::out_of_range("ROM address out of bounds: " + std::to_string(addr));
}
source_mem = ctx->rom.mem;
mem_size = ctx->rom.size;
}
switch(arg_count){ size_t max_len = mem_size - addr;
case 0: const char* str_start = reinterpret_cast<const char*>(&source_mem[addr]);
puts(working_buf);
const char* null_pos = static_cast<const char*>(
std::memchr(str_start, '\0', max_len)
);
if (null_pos) {
return std::string(str_start, null_pos - str_start);
} else {
return std::string(str_start, max_len);
}
}
// Formatter avec nombre variable d'arguments (non statique)
std::string FormatString(const std::string& format,
const std::vector<uint32_t>& args)
{
if (args.empty()) {
return format;
}
std::vector<char> buffer(1024);
int result = -1;
switch (args.size()) {
case 1:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0]);
break;
case 2:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1]);
break;
case 3:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1], args[2]);
break;
case 4:
result = std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0], args[1], args[2], args[3]);
break;
default:
throw std::runtime_error("Too many arguments for printf (max 4)");
}
if (result < 0) {
throw std::runtime_error("Error formatting string");
}
if (static_cast<size_t>(result) >= buffer.size()) {
buffer.resize(result + 1);
switch (args.size()) {
case 1:
std::snprintf(buffer.data(), buffer.size(),
format.c_str(), args[0]);
break; break;
case 1: case 2:
printf(working_buf, ctx->registers[R2]); std::snprintf(buffer.data(), buffer.size(),
puts(""); format.c_str(), args[0], args[1]);
break; break;
case 2: case 3:
printf(working_buf, ctx->registers[R2], ctx->registers[R3]); std::snprintf(buffer.data(), buffer.size(),
puts(""); format.c_str(), args[0], args[1], args[2]);
break; break;
case 3: case 4:
printf(working_buf, ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]); std::snprintf(buffer.data(), buffer.size(),
puts(""); format.c_str(), args[0], args[1], args[2], args[3]);
break;
default:
break; break;
} }
}
// WAIT (sleep)
else if (code == 5)
{
std::this_thread::sleep_for(std::chrono::milliseconds(ctx->registers[R0]));
} }
return retCode; return std::string(buffer.data());
} }
// Handler de syscall (méthode membre, non statique)
uint8_t HandleSyscall(chip32_ctx_t *ctx, uint8_t code)
{
try {
if (code == 4) // Printf
{
std::string format = GetStringFromMemory(ctx, ctx->registers[R0]);
int arg_count = ctx->registers[R1];
std::vector<uint32_t> args;
for (int i = 0; i < arg_count && i < 4; ++i) {
args.push_back(ctx->registers[R2 + i]);
}
printOutput = FormatString(format, args);
std::cout << "[SYSCALL PRINT] " << printOutput << std::endl;
}
else if (code == 5) // WAIT
{
std::this_thread::sleep_for(
std::chrono::milliseconds(ctx->registers[R0])
);
}
else
{
std::cerr << "Unknown syscall code: " << static_cast<int>(code) << std::endl;
return SYSCALL_RET_ERROR;
}
return SYSCALL_RET_OK;
} catch (const std::exception& e) {
std::cerr << "Syscall error: " << e.what() << std::endl;
return SYSCALL_RET_ERROR;
}
}
void QuickExecute(const std::string &assemblyCode) void QuickExecute(const std::string &assemblyCode)
{ {
std::vector<uint8_t> program; std::vector<uint8_t> program;
Chip32::Assembler assembler; Chip32::Assembler assembler;
Chip32::Result result; Chip32::Result result;
uint8_t data[8*1024]; std::vector<uint8_t> data(8*1024);
parseResult = assembler.Parse(assemblyCode); parseResult = assembler.Parse(assemblyCode);
std::cout << assembler.GetLastError().ToString() << std::endl; std::cout << assembler.GetLastError().ToString() << std::endl;
buildResult = assembler.BuildBinary(program, result); buildResult = assembler.BuildBinary(program, result);
result.Print(); result.Print();
chip32_ctx_t chip32_ctx; chip32_ctx_t chip32_ctx;
chip32_ctx.stack_size = 512; chip32_ctx.stack_size = 512;
chip32_ctx.rom.mem = program.data(); chip32_ctx.rom.mem = program.data();
chip32_ctx.rom.addr = 0; chip32_ctx.rom.addr = 0;
chip32_ctx.rom.size = program.size(); chip32_ctx.rom.size = program.size();
chip32_ctx.ram.mem = data.data();
chip32_ctx.ram.addr = 40 * 1024;
chip32_ctx.ram.size = data.size();
chip32_ctx.ram.mem = data; // Utiliser le wrapper statique qui appelle notre fonction membre
chip32_ctx.ram.addr = 40 *1024; chip32_ctx.syscall = SyscallWrapper;
chip32_ctx.ram.size = sizeof(data); chip32_ctx.user_data = this; // Stocker le pointeur vers cette instance
chip32_ctx.syscall = story_player_syscall;
chip32_initialize(&chip32_ctx); chip32_initialize(&chip32_ctx);
Instr mainLine; Instr mainLine;
if (assembler.GetMain(mainLine)) if (assembler.GetMain(mainLine)) {
{
// set pointer counter to the main line
chip32_ctx.registers[PC] = mainLine.addr; chip32_ctx.registers[PC] = mainLine.addr;
} }
runResult = chip32_run(&chip32_ctx); runResult = chip32_run(&chip32_ctx);
} }
private:
// std::function contenant le bind
std::function<uint8_t(chip32_ctx_t*, uint8_t)> m_syscallHandler;
// Wrapper statique qui récupère l'instance et appelle la méthode membre
static uint8_t SyscallWrapper(chip32_ctx_t *ctx, uint8_t code)
{
if (!ctx || !ctx->user_data) {
return SYSCALL_RET_ERROR;
}
Machine* instance = static_cast<Machine*>(ctx->user_data);
return instance->HandleSyscall(ctx, code);
}
}; };
} // namespace Chip32 } // namespace Chip32

View file

@ -201,7 +201,7 @@ typedef uint8_t (*syscall_t)(chip32_ctx_t *, uint8_t);
#define SYSCALL_RET_OK 0 ///< Default state, continue execution immediately #define SYSCALL_RET_OK 0 ///< Default state, continue execution immediately
#define SYSCALL_RET_WAIT_EV 1 ///< Sets the VM in wait for event state #define SYSCALL_RET_WAIT_EV 1 ///< Sets the VM in wait for event state
#define SYSCALL_RET_ERROR 2
struct chip32_ctx_t struct chip32_ctx_t
{ {
@ -213,6 +213,7 @@ struct chip32_ctx_t
uint32_t max_instr; uint32_t max_instr;
uint32_t registers[REGISTER_COUNT]; uint32_t registers[REGISTER_COUNT];
syscall_t syscall; syscall_t syscall;
void* user_data;
}; };

View file

@ -43,7 +43,6 @@ public:
virtual std::shared_ptr<IStoryProject> GetCurrentProject() = 0; virtual std::shared_ptr<IStoryProject> GetCurrentProject() = 0;
// Node interaction // Node interaction
virtual void CompileNodes(bool compileonly) = 0;
virtual void BuildCode(bool compileonly) = 0; virtual void BuildCode(bool compileonly) = 0;
virtual void SetExternalSourceFile(const std::string &filename) = 0; virtual void SetExternalSourceFile(const std::string &filename) = 0;
virtual void LoadBinaryStory(const std::string &filename) = 0; virtual void LoadBinaryStory(const std::string &filename) = 0;

View file

@ -1,509 +0,0 @@
#pragma once
#include "ast_builder.h"
#include "assembly_generator.h"
#include "call_function_node.h"
#include <algorithm>
class AssemblyGeneratorChip32 : public AssemblyGenerator
{
public:
AssemblyGeneratorChip32(const GeneratorContext& context)
: AssemblyGenerator(context)
, m_currentContext(FunctionContext::MAIN_PROGRAM)
{
}
virtual ~AssemblyGeneratorChip32() = default;
void GenerateNodeCode(std::shared_ptr<ASTNode> node, bool isDataPath = false) override
{
if (!node) return;
if (m_context.debugOutput) {
AddComment("Node: " + node->node->GetTypeName() + " (ID: " + node->node->GetId() + ")");
}
// Node label
m_assembly << node->node->GetMyEntryLabel() << ":\n";
if (node->IsType<OperatorNode>()) {
GenerateOperatorNode(node);
}
else if (node->IsType<FunctionEntryNode>()) {
// Détecter si c'est le main ou une sous-fonction
// Weight 100 = fonction principale (main)
auto* entry = node->GetAs<FunctionEntryNode>();
m_currentContext = (entry->GetWeight() >= 100)
? FunctionContext::MAIN_PROGRAM
: FunctionContext::SUB_FUNCTION;
GenerateFunctionEntry(node);
}
else if (node->IsType<BranchNode>()) {
GenerateBranchNode(node);
}
else if (node->IsType<PrintNode>()) {
GeneratePrintNode(node);
}
else if (node->IsType<CallFunctionNode>()) {
GenerateCallFunctionNode(node);
}
// Détection automatique des fins de fonction/programme
if (node->GetChildCount() == 0)
{
if (m_currentContext == FunctionContext::MAIN_PROGRAM) {
AddComment("Program exit (automatic)");
m_assembly << " halt\n";
} else {
AddComment("Function return (automatic)");
m_assembly << " ret\n";
}
}
}
virtual void AddComment(const std::string& comment) {
m_assembly << std::string(m_depth * 4, ' ') << "; " << comment << "\n";
}
virtual void GenerateExit() override {
AddComment("Program exit");
m_assembly << " halt\n";
}
private:
enum class FunctionContext {
MAIN_PROGRAM,
SUB_FUNCTION
};
FunctionContext m_currentContext;
virtual void GenerateMain() override {
// Program entry point
m_assembly << ".main:\n";
}
void GenerateFunctionEntry(std::shared_ptr<ASTNode> node) {
auto* entry = node->GetAs<FunctionEntryNode>();
if (m_currentContext == FunctionContext::MAIN_PROGRAM) {
AddComment("Main function entry");
} else {
AddComment("Function entry");
// Si nécessaire, sauvegarder les registres
// m_assembly << " push r0\n";
// m_assembly << " push r1\n";
// etc.
}
}
void GenerateCallFunctionNode(std::shared_ptr<ASTNode> node) {
auto* callNode = node->GetAs<CallFunctionNode>();
if (!callNode) return;
std::string functionName = callNode->GetFunctionName();
AddComment("Call function: " + functionName);
m_depth++;
// Préparer les arguments si nécessaire
// Dans votre système, les variables globales sont utilisées
// donc pas besoin de passer des arguments sur la pile
// Appel de la fonction
m_assembly << " call " << GetFunctionLabel(functionName) << "\n";
// Après le retour de la fonction, les variables globales
// ont potentiellement été modifiées et sont directement accessibles
m_depth--;
}
void GenerateBranchNode(std::shared_ptr<ASTNode> node)
{
AddComment("Branch condition evaluation");
m_depth++;
auto trueBranch = node->GetChild(0);
auto falseBranch = node->GetChild(1);
// Compare result and jump
m_assembly << " pop r0\n"
<< " skipz r0\n"
<< " jump " << trueBranch->node->GetMyEntryLabel() << "\n"
<< " jump " << falseBranch->node->GetMyEntryLabel() << "\n";
m_depth--;
}
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";
// 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) {
auto* opNode = node->GetAs<OperatorNode>();
if (!opNode) return;
AddComment("Operator: " + opNode->GetOperatorSymbol());
m_depth++;
// Generate code for variables usage
int reg = 0;
for (const auto& [port, inputNode] : node->dataInputs)
{
// 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)
{
// Generate code to load the variable value
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("Variable not set in node: " + inputNode->node->GetId());
}
}
}
reg++;
}
// Generate operator code based on type
switch (opNode->GetOperationType()) {
// ===== ARITHMETIC OPERATORS =====
case OperatorNode::OperationType::ADD:
m_assembly << " pop r1\n"
<< " pop r0\n"
<< " add r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::SUBTRACT:
m_assembly << " pop r1\n"
<< " pop r0\n"
<< " sub r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::MULTIPLY:
m_assembly << " pop r1\n"
<< " pop r0\n"
<< " mul r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::DIVIDE:
m_assembly << " pop r1\n"
<< " pop r0\n"
<< " div 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 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 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 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 r1\n"
<< " pop r0\n"
<< " lt r0, r0, r1 ; r0 = (r0 < r1 ? 1 : 0)\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::GREATER_EQUAL:
// >= 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:
// <= 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:
// 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:
// 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"
<< " ; 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 r1\n"
<< " pop r0\n"
<< " and r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::BITWISE_OR:
m_assembly << " pop r1\n"
<< " pop r0\n"
<< " or r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::BITWISE_XOR:
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 r1\n"
<< " pop r0\n"
<< " shl r0, r1\n"
<< " push r0\n";
break;
case OperatorNode::OperationType::RIGHT_SHIFT:
m_assembly << " pop r1\n"
<< " pop r0\n"
<< " shr r0, r1\n"
<< " push r0\n";
break;
default:
throw std::runtime_error("Unsupported operator type");
}
m_depth--;
}
// Helper pour générer le label d'une fonction à partir de son nom
std::string GetFunctionLabel(const std::string& functionName) {
// Convertir le nom de fonction en label valide
// Par exemple: "MyFunction" -> ".func_MyFunction"
return ".func_" + functionName;
}
virtual void Visit(const std::shared_ptr<Variable> v) override
{
if (v->IsConstant())
{
if (v->GetValueType() == Variable::ValueType::STRING)
{
m_assembly << "$" << v->GetLabel() << " DC8, \"" << v->GetValue<std::string>() << "\" ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::INTEGER)
{
m_assembly << "$" << v->GetLabel() << " DC32, " << v->GetValue<int>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::FLOAT)
{
m_assembly << "$" << v->GetLabel() << " DC32, " << v->GetValue<float>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::BOOL)
{
m_assembly << "$" << v->GetLabel() << " DCB, " << (v->GetValue<bool>() ? "1" : "0") << " ; " << v->GetVariableName() << "\n";
}
}
}
virtual void GenerateVariable(const std::shared_ptr<Variable> v)
{
if (v->GetValueType() == Variable::ValueType::STRING)
{
m_assembly << "$" << v->GetLabel() << " DV8, \"" << v->GetValue<std::string>() << "\" ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::INTEGER)
{
m_assembly << "$" << v->GetLabel() << " DV32, " << v->GetValue<int>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::FLOAT)
{
m_assembly << "$" << v->GetLabel() << " DV32, " << v->GetValue<float>() << " ; " << v->GetVariableName() << "\n";
}
else if (v->GetValueType() == Variable::ValueType::BOOL)
{
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

@ -0,0 +1,621 @@
// ===================================================================
// assembly_generator_chip32_tac.h
// IMPLÉMENTATION COMPLÈTE avec tous les types gérés
// ===================================================================
#pragma once
#include "assembly_generator.h"
#include "tac.h"
#include "print_node.h"
#include "operator_node.h"
#include "variable_node.h"
#include "branch_node.h"
#include "function_entry_node.h"
#include "call_function_node.h"
#include <algorithm>
#include <set>
class AssemblyGeneratorChip32TAC : public AssemblyGenerator
{
public:
AssemblyGeneratorChip32TAC(const GeneratorContext& context)
: AssemblyGenerator(context)
, m_currentContext(FunctionContext::MAIN_PROGRAM)
{
}
virtual ~AssemblyGeneratorChip32TAC() = default;
// ===================================================================
// WORKFLOW COMPLET : Cette méthode orchestre tout
// ===================================================================
void GenerateCompleteProgram(const std::vector<std::shared_ptr<BaseNode>>& nodes,
const std::vector<std::shared_ptr<ASTNode>>& astNodes)
{
Reset();
// === ÉTAPE 1 : HEADER ===
GenerateHeader();
// === ÉTAPE 2 : SECTION DATA ===
StartSection(Section::DATA);
// Générer les variables globales
GenerateGlobalVariables();
// Générer les variables des nœuds (format strings, etc.)
GenerateNodesVariables(nodes);
// === ÉTAPE 3 : GÉNÉRATION TAC ===
m_tacGenerator = std::make_unique<TACGenerator>();
m_tacProgram = m_tacGenerator->Generate(astNodes);
// DEBUG : Afficher le TAC généré
if (m_context.debugOutput) {
std::cout << "\n" << m_tacProgram.ToString() << std::endl;
}
// Générer les temporaires TAC qui seront en RAM
GenerateTACTemporaries();
m_assembly << "\n";
// === ÉTAPE 4 : SECTION TEXT (à partir du TAC) ===
StartSection(Section::TEXT);
GenerateFromTAC();
// === ÉTAPE 5 : EXIT ===
GenerateExit();
}
virtual void GenerateMain() override {
m_assembly << ".main:\n";
}
virtual void AddComment(const std::string& comment) override {
m_assembly << std::string(m_depth * 4, ' ') << "; " << comment << "\n";
}
virtual void GenerateExit() override {
AddComment("Program exit");
m_assembly << " halt\n";
}
// Note: Cette méthode ne sera PAS utilisée car on génère depuis le TAC
virtual void GenerateNodeCode(std::shared_ptr<ASTNode> node, bool isDataPath = false) override {
// Cette méthode n'est plus utilisée avec l'approche TAC
// Tout est géré via GenerateFromTAC()
// On peut laisser une implémentation vide ou lancer une exception
if (m_context.debugOutput) {
AddComment("WARNING: GenerateNodeCode called but not used in TAC mode");
}
}
void GenerateTACToAssembly(const TACProgram& tac) {
m_tacProgram = tac;
// Générer les temporaires si nécessaire
GenerateTACTemporaries();
// Convertir le TAC en assembleur
for (const auto& instr : m_tacProgram.GetInstructions()) {
GenerateTACInstruction(instr);
}
}
// Exposer l'assembly stream pour écrire directement
std::stringstream& GetAssembly() { return m_assembly; }
private:
enum class FunctionContext {
MAIN_PROGRAM,
SUB_FUNCTION
};
FunctionContext m_currentContext;
std::unique_ptr<TACGenerator> m_tacGenerator;
TACProgram m_tacProgram;
// Map des temporaires TAC vers leur localisation
struct TempLocation {
enum Type { REGISTER, MEMORY };
Type type;
std::string location; // "t0" ou "$temp_label"
};
std::map<std::string, TempLocation> m_tempLocations;
int m_nextTempReg = 0; // Prochain registre t0-t9 disponible
int m_tempVarCounter = 0; // Compteur pour variables temporaires en RAM
// ===================================================================
// GÉNÉRATION DE LA SECTION DATA
// ===================================================================
void GenerateTACTemporaries() {
// Analyser le TAC pour déterminer quels temporaires ont besoin de RAM
std::set<std::string> allTemps;
for (const auto& instr : m_tacProgram.GetInstructions()) {
if (instr->GetDest() &&
instr->GetDest()->GetType() == TACOperand::Type::TEMPORARY) {
allTemps.insert(instr->GetDest()->GetValue());
}
if (instr->GetOp1() &&
instr->GetOp1()->GetType() == TACOperand::Type::TEMPORARY) {
allTemps.insert(instr->GetOp1()->GetValue());
}
if (instr->GetOp2() &&
instr->GetOp2()->GetType() == TACOperand::Type::TEMPORARY) {
allTemps.insert(instr->GetOp2()->GetValue());
}
}
if (m_context.debugOutput && !allTemps.empty()) {
AddComment("TAC Temporaries: " + std::to_string(allTemps.size()) + " total");
}
// Allouer les 10 premiers dans des registres, le reste en RAM
int tempIndex = 0;
for (const auto& temp : allTemps) {
if (tempIndex < 10) {
// Utiliser un registre t0-t9
m_tempLocations[temp] = {
TempLocation::REGISTER,
"t" + std::to_string(tempIndex)
};
if (m_context.debugOutput) {
AddComment(" " + temp + " -> register t" + std::to_string(tempIndex));
}
} else {
// Créer une variable en RAM
std::string varLabel = "temp_" + std::to_string(m_tempVarCounter++);
m_assembly << "$" << varLabel << " DV32, 0 ; TAC temporary "
<< temp << "\n";
m_tempLocations[temp] = {
TempLocation::MEMORY,
varLabel
};
if (m_context.debugOutput) {
AddComment(" " + temp + " -> memory $" + varLabel);
}
}
tempIndex++;
}
}
// ===================================================================
// GÉNÉRATION DE LA SECTION TEXT À PARTIR DU TAC
// ===================================================================
void GenerateFromTAC() {
for (const auto& instr : m_tacProgram.GetInstructions()) {
GenerateTACInstruction(instr);
}
}
void GenerateTACInstruction(std::shared_ptr<TACInstruction> instr) {
if (m_context.debugOutput) {
AddComment("TAC: " + instr->ToString());
}
switch (instr->GetOpCode()) {
case TACInstruction::OpCode::ADD:
case TACInstruction::OpCode::SUB:
case TACInstruction::OpCode::MUL:
case TACInstruction::OpCode::DIV:
GenerateBinaryOp(instr);
break;
case TACInstruction::OpCode::EQ:
case TACInstruction::OpCode::NE:
case TACInstruction::OpCode::GT:
case TACInstruction::OpCode::LT:
case TACInstruction::OpCode::GE:
case TACInstruction::OpCode::LE:
GenerateComparison(instr);
break;
case TACInstruction::OpCode::COPY:
GenerateCopy(instr);
break;
case TACInstruction::OpCode::LOAD:
GenerateLoad(instr);
break;
case TACInstruction::OpCode::STORE:
GenerateStore(instr);
break;
case TACInstruction::OpCode::LABEL:
m_assembly << instr->GetDest()->ToString() << ":\n";
break;
case TACInstruction::OpCode::GOTO:
m_assembly << " jump " << instr->GetDest()->ToString() << "\n";
break;
case TACInstruction::OpCode::IF_FALSE:
GenerateIfFalse(instr);
break;
case TACInstruction::OpCode::IF_TRUE:
GenerateIfTrue(instr);
break;
case TACInstruction::OpCode::PARAM:
GenerateParam(instr);
break;
case TACInstruction::OpCode::PRINT:
GeneratePrint(instr);
break;
case TACInstruction::OpCode::CALL:
GenerateCall(instr);
break;
case TACInstruction::OpCode::RETURN:
m_assembly << " ret\n";
break;
case TACInstruction::OpCode::NOP:
m_assembly << " nop\n";
break;
default:
AddComment("WARNING: Unsupported TAC instruction");
}
}
// ===================================================================
// HELPERS : Charger une opérande dans un registre
// ===================================================================
std::string LoadOperand(std::shared_ptr<TACOperand> op, const std::string& targetReg) {
if (!op) return targetReg;
switch (op->GetType()) {
case TACOperand::Type::CONSTANT:
m_assembly << " lcons " << targetReg << ", " << op->GetValue() << "\n";
return targetReg;
case TACOperand::Type::VARIABLE:
m_assembly << " load " << targetReg << ", $" << op->GetValue() << ", 4\n";
return targetReg;
case TACOperand::Type::TEMPORARY: {
auto it = m_tempLocations.find(op->GetValue());
if (it == m_tempLocations.end()) {
throw std::runtime_error("Temporary not found: " + op->GetValue());
}
if (it->second.type == TempLocation::REGISTER) {
// Déjà dans un registre, copier si nécessaire
if (it->second.location != targetReg) {
m_assembly << " mov " << targetReg << ", "
<< it->second.location << "\n";
}
return targetReg;
} else {
// Charger depuis la RAM
m_assembly << " load " << targetReg << ", $"
<< it->second.location << ", 4\n";
return targetReg;
}
}
case TACOperand::Type::REGISTER:
if (op->GetValue() != targetReg) {
m_assembly << " mov " << targetReg << ", "
<< op->GetValue() << "\n";
}
return targetReg;
case TACOperand::Type::LABEL:
// Pour les labels, on utilise lcons avec l'adresse
m_assembly << " lcons " << targetReg << ", "
<< op->ToString() << "\n";
return targetReg;
default:
return targetReg;
}
}
void StoreResult(std::shared_ptr<TACOperand> dest, const std::string& sourceReg) {
if (!dest) return;
if (dest->GetType() == TACOperand::Type::TEMPORARY) {
auto it = m_tempLocations.find(dest->GetValue());
if (it == m_tempLocations.end()) {
throw std::runtime_error("Temporary not found: " + dest->GetValue());
}
if (it->second.type == TempLocation::REGISTER) {
// Stocker dans un registre
if (it->second.location != sourceReg) {
m_assembly << " mov " << it->second.location << ", "
<< sourceReg << "\n";
}
} else {
// Stocker en RAM
m_assembly << " store $" << it->second.location << ", "
<< sourceReg << ", 4\n";
}
} else if (dest->GetType() == TACOperand::Type::VARIABLE) {
m_assembly << " store $" << dest->GetValue() << ", "
<< sourceReg << ", 4\n";
} else if (dest->GetType() == TACOperand::Type::REGISTER) {
if (dest->GetValue() != sourceReg) {
m_assembly << " mov " << dest->GetValue() << ", "
<< sourceReg << "\n";
}
}
}
// ===================================================================
// GÉNÉRATION DES INSTRUCTIONS
// ===================================================================
void GenerateBinaryOp(std::shared_ptr<TACInstruction> instr) {
// Charger les opérandes
LoadOperand(instr->GetOp1(), "r0");
LoadOperand(instr->GetOp2(), "r1");
// Effectuer l'opération
std::string op;
switch (instr->GetOpCode()) {
case TACInstruction::OpCode::ADD: op = "add"; break;
case TACInstruction::OpCode::SUB: op = "sub"; break;
case TACInstruction::OpCode::MUL: op = "mul"; break;
case TACInstruction::OpCode::DIV: op = "div"; break;
default: return;
}
m_assembly << " " << op << " r0, r1\n";
// Stocker le résultat
StoreResult(instr->GetDest(), "r0");
}
void GenerateComparison(std::shared_ptr<TACInstruction> instr) {
// Charger les opérandes
LoadOperand(instr->GetOp1(), "r0");
LoadOperand(instr->GetOp2(), "r1");
// Effectuer la comparaison
switch (instr->GetOpCode()) {
case TACInstruction::OpCode::EQ:
m_assembly << " eq r0, r0, r1\n";
break;
case TACInstruction::OpCode::NE:
m_assembly << " eq r0, r0, r1\n";
m_assembly << " lcons r2, 1\n";
m_assembly << " xor r0, r2\n";
break;
case TACInstruction::OpCode::GT:
m_assembly << " gt r0, r0, r1\n";
break;
case TACInstruction::OpCode::LT:
m_assembly << " lt r0, r0, r1\n";
break;
case TACInstruction::OpCode::GE:
// >= est NOT(<)
m_assembly << " lt r0, r0, r1\n";
m_assembly << " lcons r2, 1\n";
m_assembly << " xor r0, r2\n";
break;
case TACInstruction::OpCode::LE:
// <= est NOT(>)
m_assembly << " gt r0, r0, r1\n";
m_assembly << " lcons r2, 1\n";
m_assembly << " xor r0, r2\n";
break;
default: return;
}
// Stocker le résultat
StoreResult(instr->GetDest(), "r0");
}
void GenerateCopy(std::shared_ptr<TACInstruction> instr) {
LoadOperand(instr->GetOp1(), "r0");
StoreResult(instr->GetDest(), "r0");
}
void GenerateLoad(std::shared_ptr<TACInstruction> instr) {
// dest = *op1
LoadOperand(instr->GetOp1(), "r0"); // r0 contient l'adresse
m_assembly << " load r1, @r0, 4\n";
StoreResult(instr->GetDest(), "r1");
}
void GenerateStore(std::shared_ptr<TACInstruction> instr) {
// *dest = op1
LoadOperand(instr->GetOp1(), "r0"); // r0 contient la valeur
LoadOperand(instr->GetDest(), "r1"); // r1 contient l'adresse
m_assembly << " store @r1, r0, 4\n";
}
void GenerateIfFalse(std::shared_ptr<TACInstruction> instr) {
LoadOperand(instr->GetOp1(), "r0");
m_assembly << " skipz 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 << " jump " << instr->GetDest()->ToString() << "\n";
}
std::vector<std::shared_ptr<TACOperand>> m_printParams;
void GenerateParam(std::shared_ptr<TACInstruction> instr) {
// Accumuler les paramètres pour le prochain PRINT/CALL
m_printParams.push_back(instr->GetDest());
}
void GeneratePrint(std::shared_ptr<TACInstruction> instr) {
// Sauvegarder r0, r1
m_assembly << " push r0\n";
m_assembly << " push r1\n";
// Sauvegarder les registres d'arguments selon le nombre de paramètres
int paramCount = m_printParams.size();
for (int i = 0; i < std::min(4, paramCount); i++) {
m_assembly << " push r" << (i + 2) << "\n";
}
// Charger les paramètres dans r2, r3, r4, r5
for (size_t i = 0; i < m_printParams.size() && i < 4; i++) {
LoadOperand(m_printParams[i], "r" + std::to_string(i + 2));
}
// Charger la chaîne de format dans r0
m_assembly << " lcons r0, $" << instr->GetDest()->GetValue() << "\n";
// Charger le nombre d'arguments dans r1
m_assembly << " lcons r1, " << paramCount << "\n";
// Syscall
m_assembly << " syscall 4\n";
// Restaurer les registres
for (int i = std::min(4, paramCount) - 1; i >= 0; i--) {
m_assembly << " pop r" << (i + 2) << "\n";
}
m_assembly << " pop r1\n";
m_assembly << " pop r0\n";
// Vider la liste des paramètres
m_printParams.clear();
}
void GenerateCall(std::shared_ptr<TACInstruction> instr) {
// Générer l'appel de fonction
std::string functionName = instr->GetOp1() ?
instr->GetOp1()->ToString() :
instr->GetDest()->ToString();
m_assembly << " call " << functionName << "\n";
// Si on a une destination, sauvegarder le résultat
if (instr->GetDest() && instr->GetOp1()) {
// Le résultat est dans r0 par convention
StoreResult(instr->GetDest(), "r0");
}
}
// ===================================================================
// HELPER : Obtenir la taille d'une variable
// ===================================================================
int GetVariableSize(std::shared_ptr<Variable> var) {
if (!var) return 4;
switch (var->GetValueType()) {
case Variable::ValueType::BOOL:
return 1;
case Variable::ValueType::INTEGER:
case Variable::ValueType::FLOAT:
return 4;
case Variable::ValueType::STRING:
return 1; // Par caractère
default:
return 4;
}
}
// ===================================================================
// IMPLÉMENTATIONS DES MÉTHODES VIRTUELLES - SECTION DATA
// ===================================================================
virtual void Visit(const std::shared_ptr<Variable> v) override {
if (!v || !v->IsConstant()) return;
switch (v->GetValueType()) {
case Variable::ValueType::STRING: {
std::string value = v->GetValue<std::string>();
// Convertir {0} {1} {2} {3} en %d
for (int i = 0; i < 4; ++i) {
std::string placeholder = "{" + std::to_string(i) + "}";
size_t pos = 0;
while ((pos = value.find(placeholder, pos)) != std::string::npos) {
value.replace(pos, placeholder.length(), "%d");
pos += 2;
}
}
m_assembly << "$" << v->GetLabel() << " DC8, \""
<< value << "\" ; " << v->GetVariableName() << "\n";
break;
}
case Variable::ValueType::INTEGER:
m_assembly << "$" << v->GetLabel() << " DC32, "
<< v->GetValue<int>() << " ; "
<< v->GetVariableName() << "\n";
break;
case Variable::ValueType::FLOAT:
m_assembly << "$" << v->GetLabel() << " DC32, "
<< v->GetValue<float>() << " ; "
<< v->GetVariableName() << "\n";
break;
case Variable::ValueType::BOOL:
m_assembly << "$" << v->GetLabel() << " DCB, "
<< (v->GetValue<bool>() ? "1" : "0") << " ; "
<< v->GetVariableName() << "\n";
break;
default:
AddComment("WARNING: Unsupported constant variable type for " +
v->GetVariableName());
break;
}
}
virtual void GenerateVariable(const std::shared_ptr<Variable> v) override {
if (!v) return;
switch (v->GetValueType()) {
case Variable::ValueType::STRING:
m_assembly << "$" << v->GetLabel() << " DV8, \""
<< v->GetValue<std::string>() << "\" ; "
<< v->GetVariableName() << "\n";
break;
case Variable::ValueType::INTEGER:
// CORRECTION : Déclarer 1 élément, pas la valeur !
m_assembly << "$" << v->GetLabel() << " DV32, 1 ; "
<< v->GetVariableName() << "\n";
break;
case Variable::ValueType::FLOAT:
m_assembly << "$" << v->GetLabel() << " DV32, 1 ; "
<< v->GetVariableName() << "\n";
break;
case Variable::ValueType::BOOL:
m_assembly << "$" << v->GetLabel() << " DVB, 1 ; "
<< v->GetVariableName() << "\n";
break;
default:
AddComment("WARNING: Unsupported variable type for " +
v->GetVariableName());
m_assembly << "$" << v->GetLabel() << " DV32, 1 ; "
<< v->GetVariableName() << " (unknown type)\n";
break;
}
}
};

View file

@ -183,14 +183,19 @@ public:
// Maintenant, on va ajouter les connexions de données // Maintenant, on va ajouter les connexions de données
for (const auto& conn : m_connections) for (const auto& conn : m_connections)
{ {
// Ne traiter que les connexions DATA_LINK
if (conn->type != Connection::DATA_LINK) {
continue;
}
auto outNode = nodeMap[conn->outNodeId]; auto outNode = nodeMap[conn->outNodeId];
auto inNode = nodeMap[conn->inNodeId]; auto inNode = nodeMap[conn->inNodeId];
// Keep variables nodes as data inputs // Ajouter TOUTES les connexions de données, pas seulement celles depuis VariableNode
if (dynamic_cast<VariableNode*>(outNode->node.get())) inNode->AddDataInput(conn->inPortIndex, outNode);
{
inNode->AddDataInput(conn->inPortIndex, outNode); // Ajouter aussi dans le sens inverse pour les dataOutputs
} outNode->AddDataOutput(conn->outPortIndex, inNode, conn->inPortIndex);
} }
// Build execution paths // Build execution paths

View file

@ -0,0 +1,706 @@
// ===================================================================
// tac.h - Three-Address Code Representation (CLEAN VERSION)
// ===================================================================
// Ce fichier contient UNIQUEMENT la représentation TAC et le générateur
// La conversion TAC → Assembleur est dans AssemblyGeneratorChip32TAC
// ===================================================================
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <map>
#include <set>
#include <sstream>
#include <algorithm>
// Forward declarations
class ASTNode;
class VariableNode;
class OperatorNode;
class PrintNode;
class BranchNode;
class FunctionEntryNode;
class CallFunctionNode;
// ===================================================================
// TAC Operand - Représente une opérande (variable, temporaire, constante)
// ===================================================================
class TACOperand {
public:
enum class Type {
VARIABLE, // Variable globale ($label)
TEMPORARY, // Variable temporaire (t0, t1, ...)
CONSTANT, // Constante numérique
LABEL, // Label de saut
REGISTER // Registre explicite (r0, r1, ...)
};
TACOperand(Type type, const std::string& value)
: m_type(type), m_value(value) {}
Type GetType() const { return m_type; }
std::string GetValue() const { return m_value; }
std::string ToString() const {
switch (m_type) {
case Type::VARIABLE: return "$" + m_value;
case Type::TEMPORARY: return "%" + m_value;
case Type::CONSTANT: return m_value;
case Type::LABEL: return "." + m_value;
case Type::REGISTER: return m_value;
}
return m_value;
}
private:
Type m_type;
std::string m_value;
};
// ===================================================================
// TAC Instruction - Une instruction à 3 adresses
// ===================================================================
class TACInstruction {
public:
enum class OpCode {
// Arithmetic
ADD, // dest = op1 + op2
SUB, // dest = op1 - op2
MUL, // dest = op1 * op2
DIV, // dest = op1 / op2
// Comparison
EQ, // dest = op1 == op2
NE, // dest = op1 != op2
GT, // dest = op1 > op2
LT, // dest = op1 < op2
GE, // dest = op1 >= op2
LE, // dest = op1 <= op2
// Logical
AND, // dest = op1 && op2
OR, // dest = op1 || op2
NOT, // dest = !op1
// Bitwise
BIT_AND, // dest = op1 & op2
BIT_OR, // dest = op1 | op2
BIT_XOR, // dest = op1 ^ op2
BIT_NOT, // dest = ~op1
// Assignment
COPY, // dest = op1
LOAD, // dest = *op1 (load from memory)
STORE, // *dest = op1 (store to memory)
// Control flow
LABEL, // Define a label
GOTO, // Unconditional jump
IF_FALSE, // if (!op1) goto label
IF_TRUE, // if (op1) goto label
// Function calls
PARAM, // Push parameter for call
CALL, // Call function (dest = result)
RETURN, // Return from function
// Special
PRINT, // Print (syscall 4)
SYSCALL, // Generic syscall
NOP // No operation
};
TACInstruction(OpCode op)
: m_opcode(op) {}
TACInstruction(OpCode op, std::shared_ptr<TACOperand> dest)
: m_opcode(op), m_dest(dest) {}
TACInstruction(OpCode op, std::shared_ptr<TACOperand> dest,
std::shared_ptr<TACOperand> op1)
: m_opcode(op), m_dest(dest), m_op1(op1) {}
TACInstruction(OpCode op, std::shared_ptr<TACOperand> dest,
std::shared_ptr<TACOperand> op1,
std::shared_ptr<TACOperand> op2)
: m_opcode(op), m_dest(dest), m_op1(op1), m_op2(op2) {}
OpCode GetOpCode() const { return m_opcode; }
std::shared_ptr<TACOperand> GetDest() const { return m_dest; }
std::shared_ptr<TACOperand> GetOp1() const { return m_op1; }
std::shared_ptr<TACOperand> GetOp2() const { return m_op2; }
std::string ToString() const {
std::stringstream ss;
switch (m_opcode) {
case OpCode::ADD:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " + " << m_op2->ToString();
break;
case OpCode::SUB:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " - " << m_op2->ToString();
break;
case OpCode::MUL:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " * " << m_op2->ToString();
break;
case OpCode::DIV:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " / " << m_op2->ToString();
break;
case OpCode::EQ:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " == " << m_op2->ToString();
break;
case OpCode::NE:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " != " << m_op2->ToString();
break;
case OpCode::GT:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " > " << m_op2->ToString();
break;
case OpCode::LT:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " < " << m_op2->ToString();
break;
case OpCode::GE:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " >= " << m_op2->ToString();
break;
case OpCode::LE:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " <= " << m_op2->ToString();
break;
case OpCode::AND:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " && " << m_op2->ToString();
break;
case OpCode::OR:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " || " << m_op2->ToString();
break;
case OpCode::NOT:
ss << m_dest->ToString() << " = !" << m_op1->ToString();
break;
case OpCode::BIT_AND:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " & " << m_op2->ToString();
break;
case OpCode::BIT_OR:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " | " << m_op2->ToString();
break;
case OpCode::BIT_XOR:
ss << m_dest->ToString() << " = " << m_op1->ToString()
<< " ^ " << m_op2->ToString();
break;
case OpCode::BIT_NOT:
ss << m_dest->ToString() << " = ~" << m_op1->ToString();
break;
case OpCode::COPY:
ss << m_dest->ToString() << " = " << m_op1->ToString();
break;
case OpCode::LOAD:
ss << m_dest->ToString() << " = *" << m_op1->ToString();
break;
case OpCode::STORE:
ss << "*" << m_dest->ToString() << " = " << m_op1->ToString();
break;
case OpCode::LABEL:
ss << m_dest->ToString() << ":";
break;
case OpCode::GOTO:
ss << "goto " << m_dest->ToString();
break;
case OpCode::IF_FALSE:
ss << "if (!" << m_op1->ToString() << ") goto " << m_dest->ToString();
break;
case OpCode::IF_TRUE:
ss << "if (" << m_op1->ToString() << ") goto " << m_dest->ToString();
break;
case OpCode::PARAM:
ss << "param " << m_dest->ToString();
break;
case OpCode::CALL:
if (m_op1) {
ss << m_dest->ToString() << " = call " << m_op1->ToString();
} else {
ss << "call " << m_dest->ToString();
}
break;
case OpCode::RETURN:
if (m_dest) {
ss << "return " << m_dest->ToString();
} else {
ss << "return";
}
break;
case OpCode::PRINT:
ss << "print " << m_dest->ToString();
if (m_op1) ss << ", " << m_op1->ToString();
if (m_op2) ss << ", ...";
break;
case OpCode::SYSCALL:
ss << "syscall " << m_dest->ToString();
break;
case OpCode::NOP:
ss << "nop";
break;
default:
ss << "??? opcode=" << static_cast<int>(m_opcode);
}
return ss.str();
}
private:
OpCode m_opcode;
std::shared_ptr<TACOperand> m_dest;
std::shared_ptr<TACOperand> m_op1;
std::shared_ptr<TACOperand> m_op2;
};
// ===================================================================
// TAC Program - Liste d'instructions TAC
// ===================================================================
class TACProgram {
public:
void AddInstruction(std::shared_ptr<TACInstruction> instr) {
m_instructions.push_back(instr);
}
const std::vector<std::shared_ptr<TACInstruction>>& GetInstructions() const {
return m_instructions;
}
std::string ToString() const {
std::stringstream ss;
ss << "=== Three-Address Code ===\n";
for (const auto& instr : m_instructions) {
ss << instr->ToString() << "\n";
}
return ss.str();
}
void Clear() {
m_instructions.clear();
}
size_t Size() const {
return m_instructions.size();
}
private:
std::vector<std::shared_ptr<TACInstruction>> m_instructions;
};
// ===================================================================
// TAC Generator - Convertit AST → TAC
// ===================================================================
// Note: Nécessite les includes des types de nœuds concrets
// ===================================================================
#include "ast_builder.h"
#include "variable_node.h"
#include "operator_node.h"
#include "print_node.h"
#include "branch_node.h"
#include "function_entry_node.h"
#include "call_function_node.h"
class TACGenerator {
public:
TACGenerator() : m_tempCounter(0), m_labelCounter(0) {}
// Génère le TAC à partir de l'AST
TACProgram Generate(const std::vector<std::shared_ptr<ASTNode>>& astNodes) {
std::cout << "\n=== TACGenerator::Generate() START ===\n";
std::cout << "Number of AST nodes received: " << astNodes.size() << "\n";
m_program.Clear();
m_tempCounter = 0;
m_labelCounter = 0;
m_nodeResults.clear();
m_visitedNodes.clear();
// Simplement appeler GenerateNode pour chaque nœud
// La gestion des doublons se fait dans GenerateNode()
for (size_t i = 0; i < astNodes.size(); i++) {
const auto& node = astNodes[i];
std::cout << "Processing AST node [" << i << "]: "
<< node->node->GetTypeName()
<< " (ID: " << node->GetId() << ")\n";
GenerateNode(node);
}
std::cout << "Total TAC instructions generated: " << m_program.Size() << "\n";
std::cout << "=== TACGenerator::Generate() END ===\n\n";
return m_program;
}
// Accesseur pour obtenir le dernier programme généré
const TACProgram& GetProgram() const {
return m_program;
}
private:
TACProgram m_program;
int m_tempCounter;
int m_labelCounter;
// Map pour garder en mémoire où sont stockés les résultats des nœuds
std::map<std::string, std::shared_ptr<TACOperand>> m_nodeResults;
// Set pour éviter de visiter deux fois le même nœud
std::set<std::string> m_visitedNodes;
// ===================================================================
// HELPERS
// ===================================================================
// Génère un nouveau temporaire
std::shared_ptr<TACOperand> NewTemp() {
return std::make_shared<TACOperand>(
TACOperand::Type::TEMPORARY,
"t" + std::to_string(m_tempCounter++)
);
}
// Génère un nouveau label
std::shared_ptr<TACOperand> NewLabel(const std::string& prefix = "L") {
return std::make_shared<TACOperand>(
TACOperand::Type::LABEL,
prefix + std::to_string(m_labelCounter++)
);
}
// ===================================================================
// GÉNÉRATION RÉCURSIVE
// ===================================================================
// Génère le code pour un nœud et retourne où est stocké son résultat
std::shared_ptr<TACOperand> GenerateNode(std::shared_ptr<ASTNode> node) {
if (!node) return nullptr;
std::cout << " GenerateNode(" << node->node->GetTypeName()
<< ", ID: " << node->GetId() << ")\n";
// NOUVEAU : Vérifier si ce nœud a déjà été complètement traité
if (m_visitedNodes.find(node->GetId()) != m_visitedNodes.end()) {
std::cout << " -> Node already fully processed, SKIPPING\n";
// Retourner le résultat en cache s'il existe
auto it = m_nodeResults.find(node->GetId());
if (it != m_nodeResults.end()) {
return it->second;
}
return nullptr;
}
// Marquer ce nœud comme visité DÈS LE DÉBUT
m_visitedNodes.insert(node->GetId());
// Vérifier si on a déjà évalué ce nœud (résultat en cache)
auto it = m_nodeResults.find(node->GetId());
if (it != m_nodeResults.end()) {
std::cout << " -> Result already cached, returning "
<< it->second->ToString() << "\n";
return it->second;
}
std::shared_ptr<TACOperand> result = nullptr;
if (node->IsType<VariableNode>()) {
std::cout << " -> Type: VariableNode\n";
result = GenerateVariableNode(node);
}
else if (node->IsType<OperatorNode>()) {
std::cout << " -> Type: OperatorNode\n";
result = GenerateOperatorNode(node);
}
else if (node->IsType<PrintNode>()) {
std::cout << " -> Type: PrintNode\n";
GeneratePrintNode(node);
}
else if (node->IsType<BranchNode>()) {
std::cout << " -> Type: BranchNode\n";
GenerateBranchNode(node);
}
else if (node->IsType<CallFunctionNode>()) {
std::cout << " -> Type: CallFunctionNode\n";
GenerateCallFunctionNode(node);
}
else if (node->IsType<FunctionEntryNode>()) {
std::cout << " -> Type: FunctionEntryNode (generating children)\n";
// Entry point, générer les enfants
for (size_t i = 0; i < node->children.size(); i++) {
std::cout << " Processing child [" << i << "]\n";
GenerateNode(node->children[i]);
}
}
// Mémoriser le résultat
if (result) {
std::cout << " -> Caching result: " << result->ToString() << "\n";
m_nodeResults[node->GetId()] = result;
}
return result;
}
// ===================================================================
// GÉNÉRATION PAR TYPE DE NŒUD
// ===================================================================
std::shared_ptr<TACOperand> GenerateVariableNode(std::shared_ptr<ASTNode> node) {
auto* varNode = node->GetAs<VariableNode>();
if (!varNode) return nullptr;
auto var = varNode->GetVariable();
if (!var) return nullptr;
// Créer une opérande qui référence la variable
return std::make_shared<TACOperand>(
TACOperand::Type::VARIABLE,
var->GetLabel()
);
}
std::shared_ptr<TACOperand> GenerateOperatorNode(std::shared_ptr<ASTNode> node) {
auto* opNode = node->GetAs<OperatorNode>();
if (!opNode) return nullptr;
// Évaluer les opérandes (récursif)
std::vector<std::shared_ptr<TACOperand>> operands;
// Collecter et trier les inputs par port index
std::vector<std::pair<unsigned int, std::shared_ptr<ASTNode>>> sortedInputs;
for (const auto& [port, inputNode] : node->dataInputs) {
sortedInputs.push_back({port, inputNode});
}
std::sort(sortedInputs.begin(), sortedInputs.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
for (const auto& [port, inputNode] : sortedInputs) {
auto operand = GenerateNode(inputNode);
if (operand) {
operands.push_back(operand);
}
}
if (operands.size() < 2) {
throw std::runtime_error("Operator needs at least 2 operands");
}
// Créer un temporaire pour le résultat
auto resultTemp = NewTemp();
// Générer l'instruction TAC appropriée
TACInstruction::OpCode opcode;
switch (opNode->GetOperationType()) {
case OperatorNode::OperationType::ADD:
opcode = TACInstruction::OpCode::ADD;
break;
case OperatorNode::OperationType::SUBTRACT:
opcode = TACInstruction::OpCode::SUB;
break;
case OperatorNode::OperationType::MULTIPLY:
opcode = TACInstruction::OpCode::MUL;
break;
case OperatorNode::OperationType::DIVIDE:
opcode = TACInstruction::OpCode::DIV;
break;
case OperatorNode::OperationType::EQUAL:
opcode = TACInstruction::OpCode::EQ;
break;
case OperatorNode::OperationType::NOT_EQUAL:
opcode = TACInstruction::OpCode::NE;
break;
case OperatorNode::OperationType::GREATER_THAN:
opcode = TACInstruction::OpCode::GT;
break;
case OperatorNode::OperationType::LESS_THAN:
opcode = TACInstruction::OpCode::LT;
break;
case OperatorNode::OperationType::GREATER_EQUAL:
opcode = TACInstruction::OpCode::GE;
break;
case OperatorNode::OperationType::LESS_EQUAL:
opcode = TACInstruction::OpCode::LE;
break;
default:
throw std::runtime_error("Unsupported operator");
}
auto instr = std::make_shared<TACInstruction>(
opcode, resultTemp, operands[0], operands[1]
);
m_program.AddInstruction(instr);
return resultTemp;
}
void GeneratePrintNode(std::shared_ptr<ASTNode> node) {
auto* printNode = node->GetAs<PrintNode>();
if (!printNode) return;
std::cout << " GeneratePrintNode: START\n";
// Créer l'opérande pour la chaîne de format
auto formatOperand = std::make_shared<TACOperand>(
TACOperand::Type::VARIABLE,
printNode->GetLabel()
);
std::cout << " Format string label: " << printNode->GetLabel() << "\n";
std::cout << " Number of data inputs: " << node->dataInputs.size() << "\n";
// Évaluer tous les arguments
std::vector<std::shared_ptr<TACOperand>> args;
// Collecter et trier les inputs par port index
std::vector<std::pair<unsigned int, std::shared_ptr<ASTNode>>> sortedInputs;
for (const auto& [port, inputNode] : node->dataInputs) {
std::cout << " Found input at port " << port
<< " -> " << inputNode->node->GetTypeName() << "\n";
sortedInputs.push_back({port, inputNode});
}
std::sort(sortedInputs.begin(), sortedInputs.end(),
[](const auto& a, const auto& b) { return a.first < b.first; });
// Générer le code pour chaque argument
for (const auto& [port, inputNode] : sortedInputs) {
std::cout << " Processing input port " << port << "\n";
auto argOperand = GenerateNode(inputNode);
if (argOperand) {
std::cout << " -> Got operand: " << argOperand->ToString() << "\n";
args.push_back(argOperand);
}
}
std::cout << " Total args collected: " << args.size() << "\n";
// Générer les instructions PARAM pour chaque argument
for (size_t i = 0; i < args.size(); i++) {
std::cout << " Generating PARAM instruction [" << i << "] for "
<< args[i]->ToString() << "\n";
auto paramInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::PARAM,
args[i]
);
m_program.AddInstruction(paramInstr);
}
// Générer l'instruction PRINT
std::cout << " Generating PRINT instruction\n";
auto printInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::PRINT,
formatOperand
);
m_program.AddInstruction(printInstr);
std::cout << " GeneratePrintNode: END (generated "
<< (args.size() + 1) << " instructions)\n";
}
void GenerateCallFunctionNode(std::shared_ptr<ASTNode> node) {
auto* callNode = node->GetAs<CallFunctionNode>();
if (!callNode) return;
std::string pageUuid = callNode->GetFunctionUuid();
std::string pageLabel = "page_" + pageUuid;
std::cout << " Generating CALL to page: " << pageLabel << "\n";
// Créer l'opérande label pour la page cible
auto targetLabel = std::make_shared<TACOperand>(
TACOperand::Type::LABEL,
pageLabel
);
// Générer l'instruction CALL
auto callInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::CALL,
targetLabel
);
m_program.AddInstruction(callInstr);
}
void GenerateBranchNode(std::shared_ptr<ASTNode> node) {
// Évaluer la condition (le BranchNode reçoit la condition sur le port 1)
auto conditionInput = node->GetDataInput(1);
if (!conditionInput) {
throw std::runtime_error("BranchNode missing condition input on port 1");
}
auto conditionOperand = GenerateNode(conditionInput);
if (!conditionOperand) {
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");
// if (!condition) goto falseLabel
auto ifInstr = std::make_shared<TACInstruction>(
TACInstruction::OpCode::IF_FALSE,
falseLabel,
conditionOperand
);
m_program.AddInstruction(ifInstr);
// True branch label
auto labelInstr1 = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
trueLabel
);
m_program.AddInstruction(labelInstr1);
// True branch code
if (node->GetChild(0)) {
GenerateNode(node->GetChild(0));
}
// goto end
auto gotoEnd = std::make_shared<TACInstruction>(
TACInstruction::OpCode::GOTO,
endLabel
);
m_program.AddInstruction(gotoEnd);
// False branch label
auto labelInstr2 = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
falseLabel
);
m_program.AddInstruction(labelInstr2);
// False branch code
if (node->GetChild(1)) {
GenerateNode(node->GetChild(1));
}
// End label
auto labelInstr3 = std::make_shared<TACInstruction>(
TACInstruction::OpCode::LABEL,
endLabel
);
m_program.AddInstruction(labelInstr3);
}
};
// ===================================================================
// FIN DU FICHIER
// ===================================================================
// Note: La conversion TAC → Assembleur est dans AssemblyGeneratorChip32TAC
// Ce fichier ne contient QUE la représentation TAC pure
// ===================================================================

View file

@ -55,21 +55,7 @@ public:
m_nodes.clear(); m_nodes.clear();
} }
void CompileNodesVariables(AssemblyGenerator &generator)
{
std::vector<std::shared_ptr<BaseNode>> nodes(m_nodes.begin(), m_nodes.end());
generator.GenerateNodesVariables(nodes);
}
void CompileNodes(AssemblyGenerator &generator)
{
std::vector<std::shared_ptr<BaseNode>> nodes(m_nodes.begin(), m_nodes.end());
std::vector<std::shared_ptr<Connection>> links(m_links.begin(), m_links.end());
ASTBuilder builder(nodes, links);
auto pathTree = builder.BuildAST();
generator.GenerateTextSection(pathTree);
}
virtual void GetNodeConnections(std::list<std::shared_ptr<Connection>> &c, const std::string &nodeId) override virtual void GetNodeConnections(std::list<std::shared_ptr<Connection>> &c, const std::string &nodeId) override
{ {

View file

@ -13,7 +13,7 @@
#include "print_node.h" #include "print_node.h"
#include "syscall_node.h" #include "syscall_node.h"
#include "sys_lib.h" #include "sys_lib.h"
#include "assembly_generator_chip32.h" #include "assembly_generator_chip32_tac.h"
#include "nodes_factory.h" #include "nodes_factory.h"
StoryProject::StoryProject(ILogger &log) StoryProject::StoryProject(ILogger &log)
@ -50,7 +50,7 @@ void StoryProject::CopyToDevice(const std::string &outputDir, NodesFactory &fact
// Generate and copy binary // Generate and copy binary
std::string code; std::string code;
GenerateScript(code); // GenerateScript(code); // FIXME
std::cout << code << std::endl; std::cout << code << std::endl;
@ -411,63 +411,109 @@ bool StoryProject::UseResource(const std::string &label)
return used; return used;
} }
bool StoryProject::GenerateScript(std::string &codeStr)
// story_project.cpp
bool StoryProject::GenerateCompleteProgram(std::string &assembly)
{ {
bool retCode = true; // === PHASE 1 : COLLECTE ===
std::stringstream code; // Collecter tous les nœuds et connexions de toutes les pages
std::vector<std::shared_ptr<BaseNode>> allNodes;
std::map<std::string, std::pair<std::vector<std::shared_ptr<BaseNode>>,
std::vector<std::shared_ptr<Connection>>
>> pageData;
// Empty resources usage for (const auto& page : m_pages) {
m_usedLabels.clear(); auto [nodesBegin, nodesEnd] = page->Nodes();
auto [linksBegin, linksEnd] = page->Links();
std::vector<std::shared_ptr<BaseNode>> pageNodes(nodesBegin, nodesEnd);
// Create generator context with current time and user std::vector<std::shared_ptr<Connection>> pageLinks(linksBegin, linksEnd);
AssemblyGenerator::GeneratorContext context(
// Ajouter tous les nœuds à la liste globale pour la section DATA
allNodes.insert(allNodes.end(), pageNodes.begin(), pageNodes.end());
// Stocker les données de chaque page
pageData[std::string(page->Uuid())] = {pageNodes, pageLinks};
}
// === PHASE 2 : GÉNÉRATION ===
AssemblyGenerator::GeneratorContext context(
m_variables, m_variables,
"2025-04-08 12:09:01", // Current UTC time "2025-01-10 15:30:00",
"story-editor", // Current user "story-project",
true, // Enable debug output true, // debug
true, // Enable optimizations true, // optimize
1024 // Stack size 1024
); );
// Create generator AssemblyGeneratorChip32TAC generator(context);
AssemblyGeneratorChip32 generator(context);
// Header
try generator.Reset();
{ generator.GenerateHeader();
generator.Reset();
// === SECTION DATA (commune à toutes les pages) ===
// Generate header comments generator.StartSection(AssemblyGenerator::Section::DATA);
generator.GenerateHeader();
// Variables globales (partagées entre toutes les pages)
// Generate text section generator.GenerateGlobalVariables();
generator.StartSection(AssemblyGenerator::Section::TEXT);
for (const auto & p : m_pages) // Constantes de tous les nœuds de toutes les pages
{ generator.GenerateNodesVariables(allNodes);
p->CompileNodes(generator);
// === SECTION TEXT (chaque page = une fonction) ===
generator.AddComment("======================= CODE =======================");
// Générer chaque page comme une fonction
bool isFirstPage = true;
for (const auto& page : m_pages) {
std::string pageUuid(page->Uuid());
auto& [nodes, connections] = pageData[pageUuid];
// Construire l'AST pour cette page
ASTBuilder builder(nodes, connections);
auto astNodes = builder.BuildAST();
// Générer le label de fonction
std::string functionLabel;
if (isFirstPage || pageUuid == MainUuid()) {
functionLabel = ".main";
isFirstPage = false;
} else {
functionLabel = ".page_" + pageUuid;
} }
// Generate data section generator.AddComment("========================================");
generator.StartSection(AssemblyGenerator::Section::DATA); generator.AddComment("Page: " + std::string(page->GetName()));
for (const auto & p : m_pages) generator.AddComment("UUID: " + pageUuid);
{ generator.AddComment("========================================");
p->CompileNodesVariables(generator); generator.GetAssembly() << functionLabel << ":\n";
// Générer le TAC pour cette page
TACGenerator tacGen;
TACProgram pageTAC = tacGen.Generate(astNodes);
if (context.debugOutput) {
std::cout << "\n=== TAC for page: " << page->GetName() << " ===\n";
std::cout << pageTAC.ToString() << std::endl;
}
// Convertir le TAC en assembleur
generator.GenerateTACToAssembly(pageTAC);
// Retour de la fonction (sauf pour main)
if (functionLabel != ".main") {
generator.GetAssembly() << " ret\n\n";
} }
generator.GenerateGlobalVariables();
generator.GenerateExit();
}
catch (const std::exception &e)
{
m_log.Log(e.what(), true);
retCode = false;
} }
// Exit du programme
generator.GenerateExit();
assembly = generator.GetAssembly().str();
codeStr = generator.GetAssembly(); return true;
return retCode;
} }
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err) bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)

View file

@ -16,6 +16,7 @@
#include "story_page.h" #include "story_page.h"
#include "story_options.h" #include "story_options.h"
#include "variable.h" #include "variable.h"
#include "assembly_generator_chip32_tac.h"
class NodesFactory; class NodesFactory;
@ -38,9 +39,14 @@ public:
return "490745ab-df4d-476d-ae27-027e94b8ee0a"; return "490745ab-df4d-476d-ae27-027e94b8ee0a";
} }
bool FindMain(Chip32::Instr &mainLine) {
return m_assembler.GetMain(mainLine);
}
bool GenerateCompleteProgram(std::string &assembly);
void New(const std::string &uuid, const std::string &library_path); void New(const std::string &uuid, const std::string &library_path);
std::filesystem::path BinaryFileName() const; std::filesystem::path BinaryFileName() const;
bool GenerateScript(std::string &codeStr);
bool GenerateBinary(const std::string &code, Chip32::Assembler::Error &err); bool GenerateBinary(const std::string &code, Chip32::Assembler::Error &err);
bool Load(ResourceManager &manager, NodesFactory &factory); bool Load(ResourceManager &manager, NodesFactory &factory);
void Save(ResourceManager &manager); void Save(ResourceManager &manager);

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,4 @@
// test_print_with_args.cpp // test_print_node.cpp
// Test unitaire pour vérifier que les arguments du Print sont bien pris en compte
#include "catch.hpp" #include "catch.hpp"
#include "print_node.h" #include "print_node.h"
#include "variable_node.h" #include "variable_node.h"
@ -8,137 +6,33 @@
#include "operator_node.h" #include "operator_node.h"
#include "connection.h" #include "connection.h"
#include "ast_builder.h" #include "ast_builder.h"
#include "assembly_generator_chip32.h" #include "assembly_generator_chip32_tac.h"
#include "chip32_machine.h" #include "chip32_machine.h"
#include "variable.h" #include "variable.h"
TEST_CASE("Print with single argument") { // ===================================================================
// Create the print node // TEST 1 : Print simple sans argument
auto printNode = std::make_shared<PrintNode>("print-node"); // ===================================================================
printNode->SetText("Compteur: %d"); TEST_CASE("Print without arguments - TAC", "[print][simple][tac]") {
std::cout << "\n=== Test: Print without arguments (TAC) ===\n";
// Create function entry // Variables (vide pour ce test)
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; 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 // Nodes
auto variableNodeCounter = std::make_shared<VariableNode>("variable-node"); auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
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); 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"); auto printNode = std::make_shared<PrintNode>("print-node");
printNode->SetText("Calcul: %d + %d = %d"); printNode->SetText("Hello World!");
// IMPORTANT: Appeler Initialize() si nécessaire après SetText() printNode->Initialize();
// printNode->Initialize(); // Si le test ne charge pas depuis JSON
std::vector<std::shared_ptr<BaseNode>> nodes = { std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry, functionEntry,
varNodeA,
varNodeB,
addNode,
printNode printNode
}; };
// Connexions // Connections
std::vector<std::shared_ptr<Connection>> connections; std::vector<std::shared_ptr<Connection>> connections;
// Execution: Entry → Print // Execution: Entry → Print
@ -150,150 +44,191 @@ TEST_CASE("Print with multiple arguments") {
execConn->type = Connection::EXECUTION_LINK; execConn->type = Connection::EXECUTION_LINK;
connections.push_back(execConn); connections.push_back(execConn);
// Data: varA → Print.arg0 // 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-print-no-args",
true, // debug (will show TAC)
true, // optimize
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("Hello World!") != std::string::npos);
REQUIRE(assembly.find("syscall 4") != std::string::npos);
REQUIRE(assembly.find("halt") != std::string::npos);
// Execute
std::cout << "\n--- Execution Output ---\n";
Chip32::Machine machine;
machine.QuickExecute(assembly);
std::cout << "\n✓ Test passed\n";
}
// ===================================================================
// TEST 2 : Print avec 4 arguments (maximum supporté)
// ===================================================================
TEST_CASE("Print with 4 arguments - TAC", "[print][args][tac]") {
std::cout << "\n=== Test: Print with 4 arguments (TAC) ===\n";
// Variables: A=10, B=5, C=3, D=2
std::vector<std::shared_ptr<Variable>> variables;
auto var_A = std::make_shared<Variable>("A");
var_A->SetIntegerValue(10);
variables.push_back(var_A);
auto var_B = std::make_shared<Variable>("B");
var_B->SetIntegerValue(5);
variables.push_back(var_B);
auto var_C = std::make_shared<Variable>("C");
var_C->SetIntegerValue(3);
variables.push_back(var_C);
auto var_D = std::make_shared<Variable>("D");
var_D->SetIntegerValue(2);
variables.push_back(var_D);
// Nodes
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
functionEntry->SetWeight(100);
auto varNodeA = std::make_shared<VariableNode>("var-node-A");
varNodeA->SetVariable(var_A);
auto varNodeB = std::make_shared<VariableNode>("var-node-B");
varNodeB->SetVariable(var_B);
auto varNodeC = std::make_shared<VariableNode>("var-node-C");
varNodeC->SetVariable(var_C);
auto varNodeD = std::make_shared<VariableNode>("var-node-D");
varNodeD->SetVariable(var_D);
auto printNode = std::make_shared<PrintNode>("print-node");
printNode->SetText("Values: A={0}, B={1}, C={2}, D={3}");
printNode->Initialize();
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry,
varNodeA, varNodeB, varNodeC, varNodeD,
printNode
};
// Connections
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: A → Print.arg0 (port 1)
auto dataConn1 = std::make_shared<Connection>(); auto dataConn1 = std::make_shared<Connection>();
dataConn1->outNodeId = varNodeA->GetId(); dataConn1->outNodeId = varNodeA->GetId();
dataConn1->outPortIndex = 0; dataConn1->outPortIndex = 0;
dataConn1->inNodeId = printNode->GetId(); dataConn1->inNodeId = printNode->GetId();
dataConn1->inPortIndex = 1; // ← CORRECTION: arg0 = port 1 (port 0 = execution) dataConn1->inPortIndex = 1;
dataConn1->type = Connection::DATA_LINK; dataConn1->type = Connection::DATA_LINK;
connections.push_back(dataConn1); connections.push_back(dataConn1);
// Data: varB → Print.arg1 // Data: B → Print.arg1 (port 2)
auto dataConn2 = std::make_shared<Connection>(); auto dataConn2 = std::make_shared<Connection>();
dataConn2->outNodeId = varNodeB->GetId(); dataConn2->outNodeId = varNodeB->GetId();
dataConn2->outPortIndex = 0; dataConn2->outPortIndex = 0;
dataConn2->inNodeId = printNode->GetId(); dataConn2->inNodeId = printNode->GetId();
dataConn2->inPortIndex = 2; // ← CORRECTION: arg1 = port 2 dataConn2->inPortIndex = 2;
dataConn2->type = Connection::DATA_LINK; dataConn2->type = Connection::DATA_LINK;
connections.push_back(dataConn2); connections.push_back(dataConn2);
// Data: varA → ADD.input0 // Data: C → Print.arg2 (port 3)
auto dataConn3 = std::make_shared<Connection>(); auto dataConn3 = std::make_shared<Connection>();
dataConn3->outNodeId = varNodeA->GetId(); dataConn3->outNodeId = varNodeC->GetId();
dataConn3->outPortIndex = 0; dataConn3->outPortIndex = 0;
dataConn3->inNodeId = addNode->GetId(); dataConn3->inNodeId = printNode->GetId();
dataConn3->inPortIndex = 0; dataConn3->inPortIndex = 3;
dataConn3->type = Connection::DATA_LINK; dataConn3->type = Connection::DATA_LINK;
connections.push_back(dataConn3); connections.push_back(dataConn3);
// Data: varB → ADD.input1 // Data: D → Print.arg3 (port 4)
auto dataConn4 = std::make_shared<Connection>(); auto dataConn4 = std::make_shared<Connection>();
dataConn4->outNodeId = varNodeB->GetId(); dataConn4->outNodeId = varNodeD->GetId();
dataConn4->outPortIndex = 0; dataConn4->outPortIndex = 0;
dataConn4->inNodeId = addNode->GetId(); dataConn4->inNodeId = printNode->GetId();
dataConn4->inPortIndex = 1; dataConn4->inPortIndex = 4;
dataConn4->type = Connection::DATA_LINK; dataConn4->type = Connection::DATA_LINK;
connections.push_back(dataConn4); connections.push_back(dataConn4);
// Data: ADD → Print.arg2 // Build AST
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); ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST(); auto pathTree = builder.BuildAST();
// Generate Assembly using TAC
AssemblyGenerator::GeneratorContext context( AssemblyGenerator::GeneratorContext context(
variables, variables,
"2025-01-10 10:00:00", "2025-01-10 10:00:00",
"test-print-multi-args", "test-print-4-args",
true, true, 1024 true, // debug (will show TAC)
true, // optimize
1024
); );
AssemblyGeneratorChip32 generator(context); AssemblyGeneratorChip32TAC tacGen(context);
generator.Reset(); tacGen.GenerateCompleteProgram(nodes, pathTree);
generator.GenerateHeader();
generator.StartSection(AssemblyGenerator::Section::DATA); std::string assembly = tacGen.GetAssembly().str();
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 ---\n";
std::cout << assembly << std::endl;
std::cout << "\n===== Generated Assembly (Multi-Args) =====\n" << assembly << std::endl; // Verify
REQUIRE(assembly.find("DATA") != std::string::npos);
REQUIRE(assembly.find("CODE") != std::string::npos);
// ===== Vérifications ===== // Verify variable declarations
REQUIRE(assembly.find("; A") != std::string::npos);
REQUIRE(assembly.find("; B") != std::string::npos);
REQUIRE(assembly.find("; C") != std::string::npos);
REQUIRE(assembly.find("; D") != std::string::npos);
// // Vérifier 3 arguments // Verify format string conversion {0} → %d
// REQUIRE(assembly.find("lcons r1, 3") != std::string::npos); REQUIRE(assembly.find("{0}") == std::string::npos);
REQUIRE(assembly.find("{1}") == std::string::npos);
REQUIRE(assembly.find("{2}") == std::string::npos);
REQUIRE(assembly.find("{3}") == std::string::npos);
// // Vérifier chargement des 3 registres // Verify syscall with 4 arguments
// REQUIRE(assembly.find("load r2") != std::string::npos); // arg0 REQUIRE(assembly.find("lcons r1, 4") != std::string::npos);
// REQUIRE(assembly.find("load r3") != std::string::npos); // arg1 REQUIRE(assembly.find("syscall 4") != std::string::npos);
// // r4 vient de l'opérateur ADD (devrait être sur la pile ou dans r4)
// Execute // Execute
std::cout << "\n--- Execution Output ---\n";
std::cout << "Expected: Values: A=10, B=5, C=3, D=2\n";
std::cout << "Actual: ";
Chip32::Machine machine; Chip32::Machine machine;
machine.QuickExecute(assembly); machine.QuickExecute(assembly);
}
TEST_CASE("Print with format placeholders") {
auto printNode = std::make_shared<PrintNode>("print-node");
// L'utilisateur utilise maintenant {0}, {1}, etc. std::cout << "\n✓ Test passed\n";
printNode->SetText("Résultat: {0} + {1} = {2}");
auto var_a = std::make_shared<Variable>("a");
var_a->SetIntegerValue(10);
auto var_b = std::make_shared<Variable>("b");
var_b->SetIntegerValue(20);
std::vector<std::shared_ptr<Variable>> variables = {var_a, var_b};
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry-node");
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);
std::vector<std::shared_ptr<BaseNode>> nodes = {
functionEntry, varNodeA, varNodeB, addNode, printNode
};
// Connexions...
std::vector<std::shared_ptr<Connection>> connections;
// [Ajouter les connexions comme dans les tests existants]
// Générer le code
ASTBuilder builder(nodes, connections);
auto pathTree = builder.BuildAST();
AssemblyGenerator::GeneratorContext context(
variables, "2025-01-10 10:00:00", "test-format", 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();
// Vérifier que le format a été converti de {0} {1} {2} en %d %d %d
REQUIRE(assembly.find("\"Résultat: %d + %d = %d\"") != std::string::npos);
std::cout << "\n===== Assembly avec format converti =====\n"
<< assembly << std::endl;
} }

View file

@ -9,34 +9,34 @@ Size=400,400
Collapsed=0 Collapsed=0
[Window][Library Manager] [Window][Library Manager]
Pos=656,26 Pos=630,26
Size=624,313 Size=650,235
Collapsed=0 Collapsed=0
DockId=0x00000003,0 DockId=0x00000002,0
[Window][Console] [Window][Console]
Pos=60,450 Pos=60,263
Size=475,270 Size=628,457
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
[Window][Emulator] [Window][Emulator]
Pos=656,26 Pos=630,26
Size=624,313 Size=650,235
Collapsed=0 Collapsed=0
DockId=0x00000003,5 DockId=0x00000002,5
[Window][Code viewer] [Window][Code viewer]
Pos=656,26 Pos=630,26
Size=624,313 Size=650,235
Collapsed=0 Collapsed=0
DockId=0x00000003,4 DockId=0x00000002,4
[Window][Resources] [Window][Resources]
Pos=656,26 Pos=630,26
Size=624,313 Size=650,235
Collapsed=0 Collapsed=0
DockId=0x00000003,1 DockId=0x00000002,1
[Window][Node editor] [Window][Node editor]
Pos=60,26 Pos=60,26
@ -50,28 +50,28 @@ Size=150,42
Collapsed=0 Collapsed=0
[Window][Variables] [Window][Variables]
Pos=537,450 Pos=690,263
Size=743,270 Size=590,457
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
[Window][CPU] [Window][CPU]
Pos=656,26 Pos=630,26
Size=624,313 Size=650,235
Collapsed=0 Collapsed=0
DockId=0x00000003,2 DockId=0x00000002,2
[Window][RAM view] [Window][RAM view]
Pos=656,26 Pos=630,26
Size=624,313 Size=650,235
Collapsed=0 Collapsed=0
DockId=0x00000003,3 DockId=0x00000002,3
[Window][Properties] [Window][Properties]
Pos=656,341 Pos=690,263
Size=624,107 Size=590,457
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000005,1
[Window][ToolBar] [Window][ToolBar]
Pos=0,26 Pos=0,26
@ -90,13 +90,13 @@ Collapsed=0
[Window][Module editor] [Window][Module editor]
Pos=60,26 Pos=60,26
Size=594,422 Size=568,235
Collapsed=0 Collapsed=0
DockId=0x00000001,0 DockId=0x00000001,0
[Window][Story editor] [Window][Story editor]
Pos=60,26 Pos=60,26
Size=594,422 Size=568,235
Collapsed=0 Collapsed=0
DockId=0x00000001,1 DockId=0x00000001,1
@ -106,8 +106,8 @@ Size=687,422
Collapsed=0 Collapsed=0
[Window][Error List] [Window][Error List]
Pos=60,450 Pos=60,263
Size=475,270 Size=628,457
Collapsed=0 Collapsed=0
DockId=0x00000004,1 DockId=0x00000004,1
@ -147,14 +147,19 @@ Column 2 Width=120
RefScale=20 RefScale=20
Column 0 Sort=0v Column 0 Sort=0v
[Docking][Data] [Table][0xED1C9288,4]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y RefScale=20
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,422 Split=X Column 0 Width=30
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=790,694 CentralNode=1 Selected=0x93ADCAAB Column 1 Width=80
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=624,694 Split=Y Selected=0x52EB28B5 Column 2 Weight=1.0000
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,476 Selected=0x63869CAF Column 3 Width=60
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,163 Selected=0x8C72BEA8
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,270 Split=X Selected=0xEA83D666 [Docking][Data]
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=475,192 Selected=0xD246D6BE DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=743,192 Selected=0x6DE9B20C DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,235 Split=X
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=1144,694 CentralNode=1 Selected=0x93ADCAAB
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=650,694 Selected=0x4B07C626
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,457 Split=X Selected=0xEA83D666
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=616,192 Selected=0xEA83D666
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=578,192 Selected=0x8C72BEA8

View file

@ -245,7 +245,7 @@ void AppController::CompileNodes(IStoryProject::Type type)
{ {
if (type == IStoryProject::Type::PROJECT_TYPE_STORY && m_story) if (type == IStoryProject::Type::PROJECT_TYPE_STORY && m_story)
{ {
if (m_story->GenerateScript(m_storyAssembly)) if (m_story->GenerateCompleteProgram(m_storyAssembly))
{ {
m_logger.Log("Nodes script generated for story."); m_logger.Log("Nodes script generated for story.");
Build(true); // Compile seulement par défaut Build(true); // Compile seulement par défaut
@ -257,10 +257,10 @@ void AppController::CompileNodes(IStoryProject::Type type)
} }
else if (type == IStoryProject::Type::PROJECT_TYPE_MODULE && m_module) else if (type == IStoryProject::Type::PROJECT_TYPE_MODULE && m_module)
{ {
if (m_module->GenerateScript(m_moduleAssembly)) if (m_module->GenerateCompleteProgram(m_moduleAssembly))
{ {
m_logger.Log("Nodes script generated for module."); m_logger.Log("Nodes script generated for module.");
BuildModule(true); // NEW: Use BuildModule instead BuildModule(true);
} }
else else
{ {
@ -303,6 +303,12 @@ void AppController::Build(bool compileonly)
{ {
m_story->SaveBinary(); m_story->SaveBinary();
chip32_initialize(&m_chip32_ctx); chip32_initialize(&m_chip32_ctx);
Chip32::Instr mainLine;
if (m_story->FindMain(mainLine)) {
m_chip32_ctx.registers[PC] = mainLine.addr;
}
m_dbg.run_result = VM_READY; m_dbg.run_result = VM_READY;
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
m_logger.Log("Build successful. VM ready."); m_logger.Log("Build successful. VM ready.");
@ -349,6 +355,12 @@ void AppController::BuildModule(bool compileonly)
if (m_module->CopyProgramTo(m_rom_data, sizeof(m_rom_data))) if (m_module->CopyProgramTo(m_rom_data, sizeof(m_rom_data)))
{ {
chip32_initialize(&m_chip32_ctx); chip32_initialize(&m_chip32_ctx);
Chip32::Instr mainLine;
if (m_module->FindMain(mainLine)) {
m_chip32_ctx.registers[PC] = mainLine.addr;
}
m_dbg.run_result = VM_READY; m_dbg.run_result = VM_READY;
UpdateVmView(); UpdateVmView();
@ -396,18 +408,6 @@ void AppController::BuildCode(bool compileonly)
Build(compileonly); Build(compileonly);
} }
void AppController::CompileNodes(bool compileonly)
{
if (m_story->GenerateScript(m_storyAssembly))
{
// m_debuggerWindow.SetScript(m_storyAssembly); // FIXME: GUI event
Build(compileonly);
}
}
void AppController::SetExternalSourceFile(const std::string &filename) void AppController::SetExternalSourceFile(const std::string &filename)
{ {
m_externalSourceFileName = filename; m_externalSourceFileName = filename;

View file

@ -87,7 +87,6 @@ public:
virtual void Next() override; virtual void Next() override;
virtual void Previous() override; virtual void Previous() override;
virtual std::string VmState() const override; virtual std::string VmState() const override;
virtual void CompileNodes(bool compileonly);
virtual void BuildCode(bool compileonly); virtual void BuildCode(bool compileonly);
virtual std::shared_ptr<IStoryProject> GetCurrentProject() override; virtual std::shared_ptr<IStoryProject> GetCurrentProject() override;

View file

@ -99,11 +99,6 @@ void EmulatorDock::Draw()
// ===== CONTRÔLES DE SCRIPT ET DEBUG ===== // ===== CONTRÔLES DE SCRIPT ET DEBUG =====
ImGui::SeparatorText("Script control and debug"); ImGui::SeparatorText("Script control and debug");
if (ImGui::Button("Build nodes"))
{
m_story.CompileNodes(true);
}
ImGui::SameLine();
if (ImGui::Button("Build code")) if (ImGui::Button("Build code"))
{ {
m_story.BuildCode(true); m_story.BuildCode(true);