mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-06 17:09:06 +01:00
Add TAC in compile pipeline
This commit is contained in:
parent
b76add6793
commit
eb08627029
17 changed files with 2807 additions and 952 deletions
|
|
@ -365,7 +365,7 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
|
|||
{
|
||||
if (i.isRamData)
|
||||
{
|
||||
result.ramUsageSize += i.dataLen * i.dataTypeSize/8;
|
||||
result.ramUsageSize += i.dataLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -373,7 +373,10 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
|
|||
{
|
||||
program.push_back(i.code.opcode);
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include "chip32_assembler.h"
|
||||
#include "chip32_macros.h"
|
||||
|
||||
// Dans chip32_machine.h
|
||||
|
||||
namespace Chip32
|
||||
{
|
||||
|
||||
|
|
@ -19,119 +21,207 @@ public:
|
|||
bool parseResult{false};
|
||||
bool buildResult{false};
|
||||
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;
|
||||
|
||||
Machine() {
|
||||
// Bind syscall handler to this instance
|
||||
m_syscallHandler = std::bind(&Machine::HandleSyscall, this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
}
|
||||
|
||||
// Lecture d'une chaîne depuis la mémoire (non statique maintenant)
|
||||
std::string GetStringFromMemory(chip32_ctx_t *ctx, uint32_t addr)
|
||||
{
|
||||
int valid = 0;
|
||||
if (!ctx) {
|
||||
throw std::runtime_error("Invalid context in GetStringFromMemory");
|
||||
}
|
||||
|
||||
// Test if address is valid
|
||||
bool isRam = (addr & 0x80000000) != 0;
|
||||
addr &= 0xFFFF;
|
||||
|
||||
const uint8_t* source_mem = nullptr;
|
||||
size_t mem_size = 0;
|
||||
|
||||
bool isRam = addr & 0x80000000;
|
||||
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
|
||||
if (isRam) {
|
||||
strcpy(&text[0], (const char *)&ctx->ram.mem[addr]);
|
||||
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 {
|
||||
strcpy(&text[0], (const char *)&ctx->rom.mem[addr]);
|
||||
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;
|
||||
}
|
||||
|
||||
return valid;
|
||||
size_t max_len = mem_size - addr;
|
||||
const char* str_start = reinterpret_cast<const char*>(&source_mem[addr]);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code)
|
||||
// Formatter avec nombre variable d'arguments (non statique)
|
||||
std::string FormatString(const std::string& format,
|
||||
const std::vector<uint32_t>& args)
|
||||
{
|
||||
uint8_t retCode = SYSCALL_RET_OK;
|
||||
char working_buf[100] = {0};
|
||||
if (args.empty()) {
|
||||
return format;
|
||||
}
|
||||
|
||||
// Printf
|
||||
if (code == 4)
|
||||
{
|
||||
// In R0: string with escaped characters
|
||||
// R1: Number of arguments
|
||||
// R2, R3 ... arguments
|
||||
std::vector<char> buffer(1024);
|
||||
int result = -1;
|
||||
|
||||
// Integers: stored in registers by values
|
||||
// Strings: first character address in register
|
||||
|
||||
get_from_memory(ctx, ctx->registers[R0], working_buf);
|
||||
int arg_count = ctx->registers[R1];
|
||||
|
||||
switch(arg_count){
|
||||
case 0:
|
||||
puts(working_buf);
|
||||
break;
|
||||
switch (args.size()) {
|
||||
case 1:
|
||||
printf(working_buf, ctx->registers[R2]);
|
||||
puts("");
|
||||
result = std::snprintf(buffer.data(), buffer.size(),
|
||||
format.c_str(), args[0]);
|
||||
break;
|
||||
case 2:
|
||||
printf(working_buf, ctx->registers[R2], ctx->registers[R3]);
|
||||
puts("");
|
||||
result = std::snprintf(buffer.data(), buffer.size(),
|
||||
format.c_str(), args[0], args[1]);
|
||||
break;
|
||||
case 3:
|
||||
printf(working_buf, ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]);
|
||||
puts("");
|
||||
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;
|
||||
case 2:
|
||||
std::snprintf(buffer.data(), buffer.size(),
|
||||
format.c_str(), args[0], args[1]);
|
||||
break;
|
||||
case 3:
|
||||
std::snprintf(buffer.data(), buffer.size(),
|
||||
format.c_str(), args[0], args[1], args[2]);
|
||||
break;
|
||||
case 4:
|
||||
std::snprintf(buffer.data(), buffer.size(),
|
||||
format.c_str(), args[0], args[1], args[2], args[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// WAIT (sleep)
|
||||
else if (code == 5)
|
||||
|
||||
return std::string(buffer.data());
|
||||
}
|
||||
|
||||
// Handler de syscall (méthode membre, non statique)
|
||||
uint8_t HandleSyscall(chip32_ctx_t *ctx, uint8_t code)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ctx->registers[R0]));
|
||||
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]);
|
||||
}
|
||||
|
||||
return retCode;
|
||||
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)
|
||||
{
|
||||
std::vector<uint8_t> program;
|
||||
Chip32::Assembler assembler;
|
||||
Chip32::Result result;
|
||||
uint8_t data[8*1024];
|
||||
std::vector<uint8_t> data(8*1024);
|
||||
|
||||
parseResult = assembler.Parse(assemblyCode);
|
||||
|
||||
std::cout << assembler.GetLastError().ToString() << std::endl;
|
||||
|
||||
|
||||
buildResult = assembler.BuildBinary(program, result);
|
||||
result.Print();
|
||||
|
||||
chip32_ctx_t chip32_ctx;
|
||||
|
||||
chip32_ctx.stack_size = 512;
|
||||
|
||||
chip32_ctx.rom.mem = program.data();
|
||||
chip32_ctx.rom.addr = 0;
|
||||
chip32_ctx.rom.size = program.size();
|
||||
|
||||
chip32_ctx.ram.mem = data;
|
||||
chip32_ctx.ram.mem = data.data();
|
||||
chip32_ctx.ram.addr = 40 * 1024;
|
||||
chip32_ctx.ram.size = sizeof(data);
|
||||
chip32_ctx.ram.size = data.size();
|
||||
|
||||
chip32_ctx.syscall = story_player_syscall;
|
||||
// Utiliser le wrapper statique qui appelle notre fonction membre
|
||||
chip32_ctx.syscall = SyscallWrapper;
|
||||
chip32_ctx.user_data = this; // Stocker le pointeur vers cette instance
|
||||
|
||||
chip32_initialize(&chip32_ctx);
|
||||
|
||||
Instr mainLine;
|
||||
if (assembler.GetMain(mainLine))
|
||||
{
|
||||
// set pointer counter to the main line
|
||||
if (assembler.GetMain(mainLine)) {
|
||||
chip32_ctx.registers[PC] = mainLine.addr;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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_WAIT_EV 1 ///< Sets the VM in wait for event state
|
||||
|
||||
#define SYSCALL_RET_ERROR 2
|
||||
|
||||
struct chip32_ctx_t
|
||||
{
|
||||
|
|
@ -213,6 +213,7 @@ struct chip32_ctx_t
|
|||
uint32_t max_instr;
|
||||
uint32_t registers[REGISTER_COUNT];
|
||||
syscall_t syscall;
|
||||
void* user_data;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ public:
|
|||
virtual std::shared_ptr<IStoryProject> GetCurrentProject() = 0;
|
||||
|
||||
// Node interaction
|
||||
virtual void CompileNodes(bool compileonly) = 0;
|
||||
virtual void BuildCode(bool compileonly) = 0;
|
||||
virtual void SetExternalSourceFile(const std::string &filename) = 0;
|
||||
virtual void LoadBinaryStory(const std::string &filename) = 0;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
};
|
||||
621
core/story-manager/src/compiler/assembly_generator_chip32_tac.h
Normal file
621
core/story-manager/src/compiler/assembly_generator_chip32_tac.h
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
@ -183,14 +183,19 @@ public:
|
|||
// Maintenant, on va ajouter les connexions de données
|
||||
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 inNode = nodeMap[conn->inNodeId];
|
||||
|
||||
// Keep variables nodes as data inputs
|
||||
if (dynamic_cast<VariableNode*>(outNode->node.get()))
|
||||
{
|
||||
// Ajouter TOUTES les connexions de données, pas seulement celles depuis VariableNode
|
||||
inNode->AddDataInput(conn->inPortIndex, outNode);
|
||||
}
|
||||
|
||||
// Ajouter aussi dans le sens inverse pour les dataOutputs
|
||||
outNode->AddDataOutput(conn->outPortIndex, inNode, conn->inPortIndex);
|
||||
}
|
||||
|
||||
// Build execution paths
|
||||
|
|
|
|||
706
core/story-manager/src/compiler/tac.h
Normal file
706
core/story-manager/src/compiler/tac.h
Normal 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
|
||||
// ===================================================================
|
||||
|
|
@ -55,21 +55,7 @@ public:
|
|||
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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
#include "print_node.h"
|
||||
#include "syscall_node.h"
|
||||
#include "sys_lib.h"
|
||||
#include "assembly_generator_chip32.h"
|
||||
#include "assembly_generator_chip32_tac.h"
|
||||
#include "nodes_factory.h"
|
||||
|
||||
StoryProject::StoryProject(ILogger &log)
|
||||
|
|
@ -50,7 +50,7 @@ void StoryProject::CopyToDevice(const std::string &outputDir, NodesFactory &fact
|
|||
|
||||
// Generate and copy binary
|
||||
std::string code;
|
||||
GenerateScript(code);
|
||||
// GenerateScript(code); // FIXME
|
||||
|
||||
std::cout << code << std::endl;
|
||||
|
||||
|
|
@ -411,63 +411,109 @@ bool StoryProject::UseResource(const std::string &label)
|
|||
return used;
|
||||
}
|
||||
|
||||
bool StoryProject::GenerateScript(std::string &codeStr)
|
||||
|
||||
// story_project.cpp
|
||||
|
||||
bool StoryProject::GenerateCompleteProgram(std::string &assembly)
|
||||
{
|
||||
bool retCode = true;
|
||||
std::stringstream code;
|
||||
// === PHASE 1 : COLLECTE ===
|
||||
// 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
|
||||
m_usedLabels.clear();
|
||||
for (const auto& page : m_pages) {
|
||||
auto [nodesBegin, nodesEnd] = page->Nodes();
|
||||
auto [linksBegin, linksEnd] = page->Links();
|
||||
|
||||
std::vector<std::shared_ptr<BaseNode>> pageNodes(nodesBegin, nodesEnd);
|
||||
std::vector<std::shared_ptr<Connection>> pageLinks(linksBegin, linksEnd);
|
||||
|
||||
// Ajouter tous les nœuds à la liste globale pour la section DATA
|
||||
allNodes.insert(allNodes.end(), pageNodes.begin(), pageNodes.end());
|
||||
|
||||
// Create generator context with current time and user
|
||||
// Stocker les données de chaque page
|
||||
pageData[std::string(page->Uuid())] = {pageNodes, pageLinks};
|
||||
}
|
||||
|
||||
// === PHASE 2 : GÉNÉRATION ===
|
||||
AssemblyGenerator::GeneratorContext context(
|
||||
m_variables,
|
||||
"2025-04-08 12:09:01", // Current UTC time
|
||||
"story-editor", // Current user
|
||||
true, // Enable debug output
|
||||
true, // Enable optimizations
|
||||
1024 // Stack size
|
||||
"2025-01-10 15:30:00",
|
||||
"story-project",
|
||||
true, // debug
|
||||
true, // optimize
|
||||
1024
|
||||
);
|
||||
|
||||
// Create generator
|
||||
AssemblyGeneratorChip32 generator(context);
|
||||
AssemblyGeneratorChip32TAC generator(context);
|
||||
|
||||
try
|
||||
{
|
||||
// Header
|
||||
generator.Reset();
|
||||
|
||||
// Generate header comments
|
||||
generator.GenerateHeader();
|
||||
|
||||
// Generate text section
|
||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||
for (const auto & p : m_pages)
|
||||
{
|
||||
p->CompileNodes(generator);
|
||||
}
|
||||
|
||||
// Generate data section
|
||||
// === SECTION DATA (commune à toutes les pages) ===
|
||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||
for (const auto & p : m_pages)
|
||||
{
|
||||
p->CompileNodesVariables(generator);
|
||||
}
|
||||
|
||||
// Variables globales (partagées entre toutes les pages)
|
||||
generator.GenerateGlobalVariables();
|
||||
|
||||
// Constantes de tous les nœuds de toutes les pages
|
||||
generator.GenerateNodesVariables(allNodes);
|
||||
|
||||
// === 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;
|
||||
}
|
||||
|
||||
generator.AddComment("========================================");
|
||||
generator.AddComment("Page: " + std::string(page->GetName()));
|
||||
generator.AddComment("UUID: " + pageUuid);
|
||||
generator.AddComment("========================================");
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
// Exit du programme
|
||||
generator.GenerateExit();
|
||||
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
m_log.Log(e.what(), true);
|
||||
retCode = false;
|
||||
}
|
||||
assembly = generator.GetAssembly().str();
|
||||
|
||||
codeStr = generator.GetAssembly();
|
||||
|
||||
return retCode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "story_page.h"
|
||||
#include "story_options.h"
|
||||
#include "variable.h"
|
||||
#include "assembly_generator_chip32_tac.h"
|
||||
|
||||
class NodesFactory;
|
||||
|
||||
|
|
@ -38,9 +39,14 @@ public:
|
|||
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);
|
||||
std::filesystem::path BinaryFileName() const;
|
||||
bool GenerateScript(std::string &codeStr);
|
||||
bool GenerateBinary(const std::string &code, Chip32::Assembler::Error &err);
|
||||
bool Load(ResourceManager &manager, NodesFactory &factory);
|
||||
void Save(ResourceManager &manager);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,4 @@
|
|||
// test_print_with_args.cpp
|
||||
// Test unitaire pour vérifier que les arguments du Print sont bien pris en compte
|
||||
|
||||
// test_print_node.cpp
|
||||
#include "catch.hpp"
|
||||
#include "print_node.h"
|
||||
#include "variable_node.h"
|
||||
|
|
@ -8,137 +6,33 @@
|
|||
#include "operator_node.h"
|
||||
#include "connection.h"
|
||||
#include "ast_builder.h"
|
||||
#include "assembly_generator_chip32.h"
|
||||
#include "assembly_generator_chip32_tac.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");
|
||||
// ===================================================================
|
||||
// TEST 1 : Print simple sans argument
|
||||
// ===================================================================
|
||||
TEST_CASE("Print without arguments - TAC", "[print][simple][tac]") {
|
||||
std::cout << "\n=== Test: Print without arguments (TAC) ===\n";
|
||||
|
||||
// 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
|
||||
// Variables (vide pour ce test)
|
||||
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");
|
||||
// Nodes
|
||||
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
||||
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
|
||||
printNode->SetText("Hello World!");
|
||||
printNode->Initialize();
|
||||
|
||||
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||
functionEntry,
|
||||
varNodeA,
|
||||
varNodeB,
|
||||
addNode,
|
||||
printNode
|
||||
};
|
||||
|
||||
// Connexions
|
||||
// Connections
|
||||
std::vector<std::shared_ptr<Connection>> connections;
|
||||
|
||||
// Execution: Entry → Print
|
||||
|
|
@ -150,150 +44,191 @@ TEST_CASE("Print with multiple arguments") {
|
|||
execConn->type = Connection::EXECUTION_LINK;
|
||||
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>();
|
||||
dataConn1->outNodeId = varNodeA->GetId();
|
||||
dataConn1->outPortIndex = 0;
|
||||
dataConn1->inNodeId = printNode->GetId();
|
||||
dataConn1->inPortIndex = 1; // ← CORRECTION: arg0 = port 1 (port 0 = execution)
|
||||
dataConn1->inPortIndex = 1;
|
||||
dataConn1->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn1);
|
||||
|
||||
// Data: varB → Print.arg1
|
||||
// Data: B → Print.arg1 (port 2)
|
||||
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->inPortIndex = 2;
|
||||
dataConn2->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn2);
|
||||
|
||||
// Data: varA → ADD.input0
|
||||
// Data: C → Print.arg2 (port 3)
|
||||
auto dataConn3 = std::make_shared<Connection>();
|
||||
dataConn3->outNodeId = varNodeA->GetId();
|
||||
dataConn3->outNodeId = varNodeC->GetId();
|
||||
dataConn3->outPortIndex = 0;
|
||||
dataConn3->inNodeId = addNode->GetId();
|
||||
dataConn3->inPortIndex = 0;
|
||||
dataConn3->inNodeId = printNode->GetId();
|
||||
dataConn3->inPortIndex = 3;
|
||||
dataConn3->type = Connection::DATA_LINK;
|
||||
connections.push_back(dataConn3);
|
||||
|
||||
// Data: varB → ADD.input1
|
||||
// Data: D → Print.arg3 (port 4)
|
||||
auto dataConn4 = std::make_shared<Connection>();
|
||||
dataConn4->outNodeId = varNodeB->GetId();
|
||||
dataConn4->outNodeId = varNodeD->GetId();
|
||||
dataConn4->outPortIndex = 0;
|
||||
dataConn4->inNodeId = addNode->GetId();
|
||||
dataConn4->inPortIndex = 1;
|
||||
dataConn4->inNodeId = printNode->GetId();
|
||||
dataConn4->inPortIndex = 4;
|
||||
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 =====
|
||||
|
||||
// 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-multi-args",
|
||||
true, true, 1024
|
||||
"test-print-4-args",
|
||||
true, // debug (will show TAC)
|
||||
true, // optimize
|
||||
1024
|
||||
);
|
||||
|
||||
AssemblyGeneratorChip32 generator(context);
|
||||
generator.Reset();
|
||||
generator.GenerateHeader();
|
||||
AssemblyGeneratorChip32TAC tacGen(context);
|
||||
tacGen.GenerateCompleteProgram(nodes, pathTree);
|
||||
|
||||
std::string assembly = tacGen.GetAssembly().str();
|
||||
|
||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||
generator.GenerateNodesVariables(nodes);
|
||||
generator.GenerateGlobalVariables();
|
||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||
generator.GenerateTextSection(pathTree);
|
||||
generator.GenerateExit();
|
||||
std::cout << "\n--- Generated Assembly ---\n";
|
||||
std::cout << assembly << std::endl;
|
||||
|
||||
std::string assembly = generator.GetAssembly();
|
||||
// Verify
|
||||
REQUIRE(assembly.find("DATA") != std::string::npos);
|
||||
REQUIRE(assembly.find("CODE") != std::string::npos);
|
||||
|
||||
std::cout << "\n===== Generated Assembly (Multi-Args) =====\n" << assembly << std::endl;
|
||||
// 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érifications =====
|
||||
// Verify format string conversion {0} → %d
|
||||
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 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)
|
||||
// Verify syscall with 4 arguments
|
||||
REQUIRE(assembly.find("lcons r1, 4") != std::string::npos);
|
||||
REQUIRE(assembly.find("syscall 4") != std::string::npos);
|
||||
|
||||
// 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;
|
||||
machine.QuickExecute(assembly);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Print with format placeholders") {
|
||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||
|
||||
// L'utilisateur utilise maintenant {0}, {1}, etc.
|
||||
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;
|
||||
|
||||
std::cout << "\n✓ Test passed\n";
|
||||
}
|
||||
|
|
@ -9,34 +9,34 @@ Size=400,400
|
|||
Collapsed=0
|
||||
|
||||
[Window][Library Manager]
|
||||
Pos=656,26
|
||||
Size=624,313
|
||||
Pos=630,26
|
||||
Size=650,235
|
||||
Collapsed=0
|
||||
DockId=0x00000003,0
|
||||
DockId=0x00000002,0
|
||||
|
||||
[Window][Console]
|
||||
Pos=60,450
|
||||
Size=475,270
|
||||
Pos=60,263
|
||||
Size=628,457
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][Emulator]
|
||||
Pos=656,26
|
||||
Size=624,313
|
||||
Pos=630,26
|
||||
Size=650,235
|
||||
Collapsed=0
|
||||
DockId=0x00000003,5
|
||||
DockId=0x00000002,5
|
||||
|
||||
[Window][Code viewer]
|
||||
Pos=656,26
|
||||
Size=624,313
|
||||
Pos=630,26
|
||||
Size=650,235
|
||||
Collapsed=0
|
||||
DockId=0x00000003,4
|
||||
DockId=0x00000002,4
|
||||
|
||||
[Window][Resources]
|
||||
Pos=656,26
|
||||
Size=624,313
|
||||
Pos=630,26
|
||||
Size=650,235
|
||||
Collapsed=0
|
||||
DockId=0x00000003,1
|
||||
DockId=0x00000002,1
|
||||
|
||||
[Window][Node editor]
|
||||
Pos=60,26
|
||||
|
|
@ -50,28 +50,28 @@ Size=150,42
|
|||
Collapsed=0
|
||||
|
||||
[Window][Variables]
|
||||
Pos=537,450
|
||||
Size=743,270
|
||||
Pos=690,263
|
||||
Size=590,457
|
||||
Collapsed=0
|
||||
DockId=0x00000005,0
|
||||
|
||||
[Window][CPU]
|
||||
Pos=656,26
|
||||
Size=624,313
|
||||
Pos=630,26
|
||||
Size=650,235
|
||||
Collapsed=0
|
||||
DockId=0x00000003,2
|
||||
DockId=0x00000002,2
|
||||
|
||||
[Window][RAM view]
|
||||
Pos=656,26
|
||||
Size=624,313
|
||||
Pos=630,26
|
||||
Size=650,235
|
||||
Collapsed=0
|
||||
DockId=0x00000003,3
|
||||
DockId=0x00000002,3
|
||||
|
||||
[Window][Properties]
|
||||
Pos=656,341
|
||||
Size=624,107
|
||||
Pos=690,263
|
||||
Size=590,457
|
||||
Collapsed=0
|
||||
DockId=0x00000006,0
|
||||
DockId=0x00000005,1
|
||||
|
||||
[Window][ToolBar]
|
||||
Pos=0,26
|
||||
|
|
@ -90,13 +90,13 @@ Collapsed=0
|
|||
|
||||
[Window][Module editor]
|
||||
Pos=60,26
|
||||
Size=594,422
|
||||
Size=568,235
|
||||
Collapsed=0
|
||||
DockId=0x00000001,0
|
||||
|
||||
[Window][Story editor]
|
||||
Pos=60,26
|
||||
Size=594,422
|
||||
Size=568,235
|
||||
Collapsed=0
|
||||
DockId=0x00000001,1
|
||||
|
||||
|
|
@ -106,8 +106,8 @@ Size=687,422
|
|||
Collapsed=0
|
||||
|
||||
[Window][Error List]
|
||||
Pos=60,450
|
||||
Size=475,270
|
||||
Pos=60,263
|
||||
Size=628,457
|
||||
Collapsed=0
|
||||
DockId=0x00000004,1
|
||||
|
||||
|
|
@ -147,14 +147,19 @@ Column 2 Width=120
|
|||
RefScale=20
|
||||
Column 0 Sort=0v
|
||||
|
||||
[Table][0xED1C9288,4]
|
||||
RefScale=20
|
||||
Column 0 Width=30
|
||||
Column 1 Width=80
|
||||
Column 2 Weight=1.0000
|
||||
Column 3 Width=60
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
|
||||
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,422 Split=X
|
||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=790,694 CentralNode=1 Selected=0x93ADCAAB
|
||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=624,694 Split=Y Selected=0x52EB28B5
|
||||
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,476 Selected=0x63869CAF
|
||||
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,163 Selected=0x8C72BEA8
|
||||
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,270 Split=X Selected=0xEA83D666
|
||||
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=475,192 Selected=0xD246D6BE
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ void AppController::CompileNodes(IStoryProject::Type type)
|
|||
{
|
||||
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.");
|
||||
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)
|
||||
{
|
||||
if (m_module->GenerateScript(m_moduleAssembly))
|
||||
if (m_module->GenerateCompleteProgram(m_moduleAssembly))
|
||||
{
|
||||
m_logger.Log("Nodes script generated for module.");
|
||||
BuildModule(true); // NEW: Use BuildModule instead
|
||||
BuildModule(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -303,6 +303,12 @@ void AppController::Build(bool compileonly)
|
|||
{
|
||||
m_story->SaveBinary();
|
||||
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;
|
||||
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
|
||||
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)))
|
||||
{
|
||||
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;
|
||||
UpdateVmView();
|
||||
|
||||
|
|
@ -396,18 +408,6 @@ void AppController::BuildCode(bool 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)
|
||||
{
|
||||
m_externalSourceFileName = filename;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ public:
|
|||
virtual void Next() override;
|
||||
virtual void Previous() override;
|
||||
virtual std::string VmState() const override;
|
||||
virtual void CompileNodes(bool compileonly);
|
||||
virtual void BuildCode(bool compileonly);
|
||||
virtual std::shared_ptr<IStoryProject> GetCurrentProject() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -99,11 +99,6 @@ void EmulatorDock::Draw()
|
|||
// ===== CONTRÔLES DE SCRIPT ET DEBUG =====
|
||||
ImGui::SeparatorText("Script control and debug");
|
||||
|
||||
if (ImGui::Button("Build nodes"))
|
||||
{
|
||||
m_story.CompileNodes(true);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Build code"))
|
||||
{
|
||||
m_story.BuildCode(true);
|
||||
|
|
|
|||
Loading…
Reference in a new issue