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)
|
if (i.isRamData)
|
||||||
{
|
{
|
||||||
result.ramUsageSize += i.dataLen * i.dataTypeSize/8;
|
result.ramUsageSize += i.dataLen;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -373,7 +373,10 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
|
||||||
{
|
{
|
||||||
program.push_back(i.code.opcode);
|
program.push_back(i.code.opcode);
|
||||||
}
|
}
|
||||||
result.constantsSize += i.compiledArgs.size();
|
else if (i.isRomData) // Seulement pour ROM DATA
|
||||||
|
{
|
||||||
|
result.constantsSize += i.compiledArgs.size();
|
||||||
|
}
|
||||||
std::copy (i.compiledArgs.begin(), i.compiledArgs.end(), std::back_inserter(program));
|
std::copy (i.compiledArgs.begin(), i.compiledArgs.end(), std::back_inserter(program));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
#include "chip32_assembler.h"
|
#include "chip32_assembler.h"
|
||||||
#include "chip32_macros.h"
|
#include "chip32_macros.h"
|
||||||
|
|
||||||
|
// Dans chip32_machine.h
|
||||||
|
|
||||||
namespace Chip32
|
namespace Chip32
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -19,119 +21,207 @@ public:
|
||||||
bool parseResult{false};
|
bool parseResult{false};
|
||||||
bool buildResult{false};
|
bool buildResult{false};
|
||||||
chip32_result_t runResult{VM_OK};
|
chip32_result_t runResult{VM_OK};
|
||||||
|
std::string printOutput;
|
||||||
|
|
||||||
static int get_from_memory(chip32_ctx_t *ctx, uint32_t addr, char *text)
|
static Machine *m_instance;
|
||||||
{
|
|
||||||
int valid = 0;
|
|
||||||
|
|
||||||
// Test if address is valid
|
Machine() {
|
||||||
|
// Bind syscall handler to this instance
|
||||||
bool isRam = addr & 0x80000000;
|
m_syscallHandler = std::bind(&Machine::HandleSyscall, this,
|
||||||
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
|
std::placeholders::_1,
|
||||||
if (isRam) {
|
std::placeholders::_2);
|
||||||
strcpy(&text[0], (const char *)&ctx->ram.mem[addr]);
|
|
||||||
} else {
|
|
||||||
strcpy(&text[0], (const char *)&ctx->rom.mem[addr]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lecture d'une chaîne depuis la mémoire (non statique maintenant)
|
||||||
|
std::string GetStringFromMemory(chip32_ctx_t *ctx, uint32_t addr)
|
||||||
static uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code)
|
|
||||||
{
|
{
|
||||||
uint8_t retCode = SYSCALL_RET_OK;
|
if (!ctx) {
|
||||||
char working_buf[100] = {0};
|
throw std::runtime_error("Invalid context in GetStringFromMemory");
|
||||||
|
}
|
||||||
|
|
||||||
// Printf
|
bool isRam = (addr & 0x80000000) != 0;
|
||||||
if (code == 4)
|
addr &= 0xFFFF;
|
||||||
{
|
|
||||||
// In R0: string with escaped characters
|
|
||||||
// R1: Number of arguments
|
|
||||||
// R2, R3 ... arguments
|
|
||||||
|
|
||||||
// Integers: stored in registers by values
|
const uint8_t* source_mem = nullptr;
|
||||||
// Strings: first character address in register
|
size_t mem_size = 0;
|
||||||
|
|
||||||
get_from_memory(ctx, ctx->registers[R0], working_buf);
|
if (isRam) {
|
||||||
int arg_count = ctx->registers[R1];
|
if (addr >= ctx->ram.size) {
|
||||||
|
throw std::out_of_range("RAM address out of bounds: " + std::to_string(addr));
|
||||||
|
}
|
||||||
|
source_mem = ctx->ram.mem;
|
||||||
|
mem_size = ctx->ram.size;
|
||||||
|
} else {
|
||||||
|
if (addr >= ctx->rom.size) {
|
||||||
|
throw std::out_of_range("ROM address out of bounds: " + std::to_string(addr));
|
||||||
|
}
|
||||||
|
source_mem = ctx->rom.mem;
|
||||||
|
mem_size = ctx->rom.size;
|
||||||
|
}
|
||||||
|
|
||||||
switch(arg_count){
|
size_t max_len = mem_size - addr;
|
||||||
case 0:
|
const char* str_start = reinterpret_cast<const char*>(&source_mem[addr]);
|
||||||
puts(working_buf);
|
|
||||||
break;
|
const char* null_pos = static_cast<const char*>(
|
||||||
|
std::memchr(str_start, '\0', max_len)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (null_pos) {
|
||||||
|
return std::string(str_start, null_pos - str_start);
|
||||||
|
} else {
|
||||||
|
return std::string(str_start, max_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatter avec nombre variable d'arguments (non statique)
|
||||||
|
std::string FormatString(const std::string& format,
|
||||||
|
const std::vector<uint32_t>& args)
|
||||||
|
{
|
||||||
|
if (args.empty()) {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> buffer(1024);
|
||||||
|
int result = -1;
|
||||||
|
|
||||||
|
switch (args.size()) {
|
||||||
|
case 1:
|
||||||
|
result = std::snprintf(buffer.data(), buffer.size(),
|
||||||
|
format.c_str(), args[0]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
result = std::snprintf(buffer.data(), buffer.size(),
|
||||||
|
format.c_str(), args[0], args[1]);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
result = std::snprintf(buffer.data(), buffer.size(),
|
||||||
|
format.c_str(), args[0], args[1], args[2]);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
result = std::snprintf(buffer.data(), buffer.size(),
|
||||||
|
format.c_str(), args[0], args[1], args[2], args[3]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("Too many arguments for printf (max 4)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
throw std::runtime_error("Error formatting string");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<size_t>(result) >= buffer.size()) {
|
||||||
|
buffer.resize(result + 1);
|
||||||
|
|
||||||
|
switch (args.size()) {
|
||||||
case 1:
|
case 1:
|
||||||
printf(working_buf, ctx->registers[R2]);
|
std::snprintf(buffer.data(), buffer.size(),
|
||||||
puts("");
|
format.c_str(), args[0]);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
printf(working_buf, ctx->registers[R2], ctx->registers[R3]);
|
std::snprintf(buffer.data(), buffer.size(),
|
||||||
puts("");
|
format.c_str(), args[0], args[1]);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
printf(working_buf, ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]);
|
std::snprintf(buffer.data(), buffer.size(),
|
||||||
puts("");
|
format.c_str(), args[0], args[1], args[2]);
|
||||||
break;
|
break;
|
||||||
default:
|
case 4:
|
||||||
|
std::snprintf(buffer.data(), buffer.size(),
|
||||||
|
format.c_str(), args[0], args[1], args[2], args[3]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
// WAIT (sleep)
|
|
||||||
else if (code == 5)
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(ctx->registers[R0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return retCode;
|
return std::string(buffer.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler de syscall (méthode membre, non statique)
|
||||||
|
uint8_t HandleSyscall(chip32_ctx_t *ctx, uint8_t code)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (code == 4) // Printf
|
||||||
|
{
|
||||||
|
std::string format = GetStringFromMemory(ctx, ctx->registers[R0]);
|
||||||
|
int arg_count = ctx->registers[R1];
|
||||||
|
|
||||||
|
std::vector<uint32_t> args;
|
||||||
|
for (int i = 0; i < arg_count && i < 4; ++i) {
|
||||||
|
args.push_back(ctx->registers[R2 + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printOutput = FormatString(format, args);
|
||||||
|
std::cout << "[SYSCALL PRINT] " << printOutput << std::endl;
|
||||||
|
}
|
||||||
|
else if (code == 5) // WAIT
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(
|
||||||
|
std::chrono::milliseconds(ctx->registers[R0])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Unknown syscall code: " << static_cast<int>(code) << std::endl;
|
||||||
|
return SYSCALL_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SYSCALL_RET_OK;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Syscall error: " << e.what() << std::endl;
|
||||||
|
return SYSCALL_RET_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QuickExecute(const std::string &assemblyCode)
|
void QuickExecute(const std::string &assemblyCode)
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> program;
|
std::vector<uint8_t> program;
|
||||||
Chip32::Assembler assembler;
|
Chip32::Assembler assembler;
|
||||||
Chip32::Result result;
|
Chip32::Result result;
|
||||||
uint8_t data[8*1024];
|
std::vector<uint8_t> data(8*1024);
|
||||||
|
|
||||||
parseResult = assembler.Parse(assemblyCode);
|
parseResult = assembler.Parse(assemblyCode);
|
||||||
|
|
||||||
std::cout << assembler.GetLastError().ToString() << std::endl;
|
std::cout << assembler.GetLastError().ToString() << std::endl;
|
||||||
|
|
||||||
|
|
||||||
buildResult = assembler.BuildBinary(program, result);
|
buildResult = assembler.BuildBinary(program, result);
|
||||||
result.Print();
|
result.Print();
|
||||||
|
|
||||||
chip32_ctx_t chip32_ctx;
|
chip32_ctx_t chip32_ctx;
|
||||||
|
|
||||||
chip32_ctx.stack_size = 512;
|
chip32_ctx.stack_size = 512;
|
||||||
|
|
||||||
chip32_ctx.rom.mem = program.data();
|
chip32_ctx.rom.mem = program.data();
|
||||||
chip32_ctx.rom.addr = 0;
|
chip32_ctx.rom.addr = 0;
|
||||||
chip32_ctx.rom.size = program.size();
|
chip32_ctx.rom.size = program.size();
|
||||||
|
chip32_ctx.ram.mem = data.data();
|
||||||
|
chip32_ctx.ram.addr = 40 * 1024;
|
||||||
|
chip32_ctx.ram.size = data.size();
|
||||||
|
|
||||||
chip32_ctx.ram.mem = data;
|
// Utiliser le wrapper statique qui appelle notre fonction membre
|
||||||
chip32_ctx.ram.addr = 40 *1024;
|
chip32_ctx.syscall = SyscallWrapper;
|
||||||
chip32_ctx.ram.size = sizeof(data);
|
chip32_ctx.user_data = this; // Stocker le pointeur vers cette instance
|
||||||
|
|
||||||
chip32_ctx.syscall = story_player_syscall;
|
|
||||||
|
|
||||||
chip32_initialize(&chip32_ctx);
|
chip32_initialize(&chip32_ctx);
|
||||||
|
|
||||||
Instr mainLine;
|
Instr mainLine;
|
||||||
if (assembler.GetMain(mainLine))
|
if (assembler.GetMain(mainLine)) {
|
||||||
{
|
|
||||||
// set pointer counter to the main line
|
|
||||||
chip32_ctx.registers[PC] = mainLine.addr;
|
chip32_ctx.registers[PC] = mainLine.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
runResult = chip32_run(&chip32_ctx);
|
runResult = chip32_run(&chip32_ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// std::function contenant le bind
|
||||||
|
std::function<uint8_t(chip32_ctx_t*, uint8_t)> m_syscallHandler;
|
||||||
|
|
||||||
|
// Wrapper statique qui récupère l'instance et appelle la méthode membre
|
||||||
|
static uint8_t SyscallWrapper(chip32_ctx_t *ctx, uint8_t code)
|
||||||
|
{
|
||||||
|
if (!ctx || !ctx->user_data) {
|
||||||
|
return SYSCALL_RET_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine* instance = static_cast<Machine*>(ctx->user_data);
|
||||||
|
return instance->HandleSyscall(ctx, code);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace Chip32
|
} // namespace Chip32
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,7 @@ typedef uint8_t (*syscall_t)(chip32_ctx_t *, uint8_t);
|
||||||
|
|
||||||
#define SYSCALL_RET_OK 0 ///< Default state, continue execution immediately
|
#define SYSCALL_RET_OK 0 ///< Default state, continue execution immediately
|
||||||
#define SYSCALL_RET_WAIT_EV 1 ///< Sets the VM in wait for event state
|
#define SYSCALL_RET_WAIT_EV 1 ///< Sets the VM in wait for event state
|
||||||
|
#define SYSCALL_RET_ERROR 2
|
||||||
|
|
||||||
struct chip32_ctx_t
|
struct chip32_ctx_t
|
||||||
{
|
{
|
||||||
|
|
@ -213,6 +213,7 @@ struct chip32_ctx_t
|
||||||
uint32_t max_instr;
|
uint32_t max_instr;
|
||||||
uint32_t registers[REGISTER_COUNT];
|
uint32_t registers[REGISTER_COUNT];
|
||||||
syscall_t syscall;
|
syscall_t syscall;
|
||||||
|
void* user_data;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ public:
|
||||||
virtual std::shared_ptr<IStoryProject> GetCurrentProject() = 0;
|
virtual std::shared_ptr<IStoryProject> GetCurrentProject() = 0;
|
||||||
|
|
||||||
// Node interaction
|
// Node interaction
|
||||||
virtual void CompileNodes(bool compileonly) = 0;
|
|
||||||
virtual void BuildCode(bool compileonly) = 0;
|
virtual void BuildCode(bool compileonly) = 0;
|
||||||
virtual void SetExternalSourceFile(const std::string &filename) = 0;
|
virtual void SetExternalSourceFile(const std::string &filename) = 0;
|
||||||
virtual void LoadBinaryStory(const std::string &filename) = 0;
|
virtual void LoadBinaryStory(const std::string &filename) = 0;
|
||||||
|
|
|
||||||
|
|
@ -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
|
// Maintenant, on va ajouter les connexions de données
|
||||||
for (const auto& conn : m_connections)
|
for (const auto& conn : m_connections)
|
||||||
{
|
{
|
||||||
|
// Ne traiter que les connexions DATA_LINK
|
||||||
|
if (conn->type != Connection::DATA_LINK) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto outNode = nodeMap[conn->outNodeId];
|
auto outNode = nodeMap[conn->outNodeId];
|
||||||
auto inNode = nodeMap[conn->inNodeId];
|
auto inNode = nodeMap[conn->inNodeId];
|
||||||
|
|
||||||
// Keep variables nodes as data inputs
|
// Ajouter TOUTES les connexions de données, pas seulement celles depuis VariableNode
|
||||||
if (dynamic_cast<VariableNode*>(outNode->node.get()))
|
inNode->AddDataInput(conn->inPortIndex, outNode);
|
||||||
{
|
|
||||||
inNode->AddDataInput(conn->inPortIndex, outNode);
|
// Ajouter aussi dans le sens inverse pour les dataOutputs
|
||||||
}
|
outNode->AddDataOutput(conn->outPortIndex, inNode, conn->inPortIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build execution paths
|
// Build execution paths
|
||||||
|
|
|
||||||
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();
|
m_nodes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompileNodesVariables(AssemblyGenerator &generator)
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<BaseNode>> nodes(m_nodes.begin(), m_nodes.end());
|
|
||||||
generator.GenerateNodesVariables(nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompileNodes(AssemblyGenerator &generator)
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<BaseNode>> nodes(m_nodes.begin(), m_nodes.end());
|
|
||||||
std::vector<std::shared_ptr<Connection>> links(m_links.begin(), m_links.end());
|
|
||||||
ASTBuilder builder(nodes, links);
|
|
||||||
auto pathTree = builder.BuildAST();
|
|
||||||
|
|
||||||
generator.GenerateTextSection(pathTree);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void GetNodeConnections(std::list<std::shared_ptr<Connection>> &c, const std::string &nodeId) override
|
virtual void GetNodeConnections(std::list<std::shared_ptr<Connection>> &c, const std::string &nodeId) override
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
#include "print_node.h"
|
#include "print_node.h"
|
||||||
#include "syscall_node.h"
|
#include "syscall_node.h"
|
||||||
#include "sys_lib.h"
|
#include "sys_lib.h"
|
||||||
#include "assembly_generator_chip32.h"
|
#include "assembly_generator_chip32_tac.h"
|
||||||
#include "nodes_factory.h"
|
#include "nodes_factory.h"
|
||||||
|
|
||||||
StoryProject::StoryProject(ILogger &log)
|
StoryProject::StoryProject(ILogger &log)
|
||||||
|
|
@ -50,7 +50,7 @@ void StoryProject::CopyToDevice(const std::string &outputDir, NodesFactory &fact
|
||||||
|
|
||||||
// Generate and copy binary
|
// Generate and copy binary
|
||||||
std::string code;
|
std::string code;
|
||||||
GenerateScript(code);
|
// GenerateScript(code); // FIXME
|
||||||
|
|
||||||
std::cout << code << std::endl;
|
std::cout << code << std::endl;
|
||||||
|
|
||||||
|
|
@ -411,63 +411,109 @@ bool StoryProject::UseResource(const std::string &label)
|
||||||
return used;
|
return used;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StoryProject::GenerateScript(std::string &codeStr)
|
|
||||||
|
// story_project.cpp
|
||||||
|
|
||||||
|
bool StoryProject::GenerateCompleteProgram(std::string &assembly)
|
||||||
{
|
{
|
||||||
bool retCode = true;
|
// === PHASE 1 : COLLECTE ===
|
||||||
std::stringstream code;
|
// Collecter tous les nœuds et connexions de toutes les pages
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> allNodes;
|
||||||
|
std::map<std::string, std::pair<std::vector<std::shared_ptr<BaseNode>>,
|
||||||
|
std::vector<std::shared_ptr<Connection>>
|
||||||
|
>> pageData;
|
||||||
|
|
||||||
// Empty resources usage
|
for (const auto& page : m_pages) {
|
||||||
m_usedLabels.clear();
|
auto [nodesBegin, nodesEnd] = page->Nodes();
|
||||||
|
auto [linksBegin, linksEnd] = page->Links();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> pageNodes(nodesBegin, nodesEnd);
|
||||||
|
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
|
||||||
AssemblyGenerator::GeneratorContext context(
|
pageData[std::string(page->Uuid())] = {pageNodes, pageLinks};
|
||||||
|
}
|
||||||
|
|
||||||
|
// === PHASE 2 : GÉNÉRATION ===
|
||||||
|
AssemblyGenerator::GeneratorContext context(
|
||||||
m_variables,
|
m_variables,
|
||||||
"2025-04-08 12:09:01", // Current UTC time
|
"2025-01-10 15:30:00",
|
||||||
"story-editor", // Current user
|
"story-project",
|
||||||
true, // Enable debug output
|
true, // debug
|
||||||
true, // Enable optimizations
|
true, // optimize
|
||||||
1024 // Stack size
|
1024
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create generator
|
AssemblyGeneratorChip32TAC generator(context);
|
||||||
AssemblyGeneratorChip32 generator(context);
|
|
||||||
|
|
||||||
try
|
// Header
|
||||||
{
|
generator.Reset();
|
||||||
generator.Reset();
|
generator.GenerateHeader();
|
||||||
|
|
||||||
// Generate header comments
|
// === SECTION DATA (commune à toutes les pages) ===
|
||||||
generator.GenerateHeader();
|
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||||
|
|
||||||
// Generate text section
|
// Variables globales (partagées entre toutes les pages)
|
||||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
generator.GenerateGlobalVariables();
|
||||||
for (const auto & p : m_pages)
|
|
||||||
{
|
// Constantes de tous les nœuds de toutes les pages
|
||||||
p->CompileNodes(generator);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate data section
|
generator.AddComment("========================================");
|
||||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
generator.AddComment("Page: " + std::string(page->GetName()));
|
||||||
for (const auto & p : m_pages)
|
generator.AddComment("UUID: " + pageUuid);
|
||||||
{
|
generator.AddComment("========================================");
|
||||||
p->CompileNodesVariables(generator);
|
generator.GetAssembly() << functionLabel << ":\n";
|
||||||
|
|
||||||
|
// Générer le TAC pour cette page
|
||||||
|
TACGenerator tacGen;
|
||||||
|
TACProgram pageTAC = tacGen.Generate(astNodes);
|
||||||
|
|
||||||
|
if (context.debugOutput) {
|
||||||
|
std::cout << "\n=== TAC for page: " << page->GetName() << " ===\n";
|
||||||
|
std::cout << pageTAC.ToString() << std::endl;
|
||||||
}
|
}
|
||||||
generator.GenerateGlobalVariables();
|
|
||||||
|
|
||||||
generator.GenerateExit();
|
// Convertir le TAC en assembleur
|
||||||
|
generator.GenerateTACToAssembly(pageTAC);
|
||||||
|
|
||||||
}
|
// Retour de la fonction (sauf pour main)
|
||||||
catch (const std::exception &e)
|
if (functionLabel != ".main") {
|
||||||
{
|
generator.GetAssembly() << " ret\n\n";
|
||||||
m_log.Log(e.what(), true);
|
}
|
||||||
retCode = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codeStr = generator.GetAssembly();
|
// Exit du programme
|
||||||
|
generator.GenerateExit();
|
||||||
|
|
||||||
return retCode;
|
assembly = generator.GetAssembly().str();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
|
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
#include "story_page.h"
|
#include "story_page.h"
|
||||||
#include "story_options.h"
|
#include "story_options.h"
|
||||||
#include "variable.h"
|
#include "variable.h"
|
||||||
|
#include "assembly_generator_chip32_tac.h"
|
||||||
|
|
||||||
class NodesFactory;
|
class NodesFactory;
|
||||||
|
|
||||||
|
|
@ -38,9 +39,14 @@ public:
|
||||||
return "490745ab-df4d-476d-ae27-027e94b8ee0a";
|
return "490745ab-df4d-476d-ae27-027e94b8ee0a";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FindMain(Chip32::Instr &mainLine) {
|
||||||
|
return m_assembler.GetMain(mainLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GenerateCompleteProgram(std::string &assembly);
|
||||||
|
|
||||||
void New(const std::string &uuid, const std::string &library_path);
|
void New(const std::string &uuid, const std::string &library_path);
|
||||||
std::filesystem::path BinaryFileName() const;
|
std::filesystem::path BinaryFileName() const;
|
||||||
bool GenerateScript(std::string &codeStr);
|
|
||||||
bool GenerateBinary(const std::string &code, Chip32::Assembler::Error &err);
|
bool GenerateBinary(const std::string &code, Chip32::Assembler::Error &err);
|
||||||
bool Load(ResourceManager &manager, NodesFactory &factory);
|
bool Load(ResourceManager &manager, NodesFactory &factory);
|
||||||
void Save(ResourceManager &manager);
|
void Save(ResourceManager &manager);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,4 @@
|
||||||
// test_print_with_args.cpp
|
// test_print_node.cpp
|
||||||
// Test unitaire pour vérifier que les arguments du Print sont bien pris en compte
|
|
||||||
|
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
#include "print_node.h"
|
#include "print_node.h"
|
||||||
#include "variable_node.h"
|
#include "variable_node.h"
|
||||||
|
|
@ -8,137 +6,33 @@
|
||||||
#include "operator_node.h"
|
#include "operator_node.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "ast_builder.h"
|
#include "ast_builder.h"
|
||||||
#include "assembly_generator_chip32.h"
|
#include "assembly_generator_chip32_tac.h"
|
||||||
#include "chip32_machine.h"
|
#include "chip32_machine.h"
|
||||||
#include "variable.h"
|
#include "variable.h"
|
||||||
|
|
||||||
TEST_CASE("Print with single argument") {
|
// ===================================================================
|
||||||
// Create the print node
|
// TEST 1 : Print simple sans argument
|
||||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
// ===================================================================
|
||||||
printNode->SetText("Compteur: %d");
|
TEST_CASE("Print without arguments - TAC", "[print][simple][tac]") {
|
||||||
|
std::cout << "\n=== Test: Print without arguments (TAC) ===\n";
|
||||||
|
|
||||||
// Create function entry
|
// Variables (vide pour ce test)
|
||||||
auto functionEntryNode = std::make_shared<FunctionEntryNode>("function-entry-node");
|
|
||||||
|
|
||||||
// IMPORTANT: Create the "counter" variable and add it to the global variables list
|
|
||||||
std::vector<std::shared_ptr<Variable>> variables;
|
std::vector<std::shared_ptr<Variable>> variables;
|
||||||
auto counterVar = std::make_shared<Variable>("counter");
|
|
||||||
counterVar->SetIntegerValue(42); // Initial value
|
|
||||||
variables.push_back(counterVar); // ← CRUCIAL: Add to global variables
|
|
||||||
|
|
||||||
// Create a variable node that references the counter variable
|
// Nodes
|
||||||
auto variableNodeCounter = std::make_shared<VariableNode>("variable-node");
|
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
||||||
variableNodeCounter->SetVariable(counterVar);
|
|
||||||
|
|
||||||
// Build the node list
|
|
||||||
std::vector<std::shared_ptr<BaseNode>> nodes;
|
|
||||||
nodes.push_back(functionEntryNode);
|
|
||||||
nodes.push_back(printNode);
|
|
||||||
nodes.push_back(variableNodeCounter);
|
|
||||||
|
|
||||||
// Create connections
|
|
||||||
std::vector<std::shared_ptr<Connection>> connections;
|
|
||||||
|
|
||||||
// Connect function entry to print node (execution flow)
|
|
||||||
auto cn1 = std::make_shared<Connection>();
|
|
||||||
cn1->inNodeId = printNode->GetId();
|
|
||||||
cn1->inPortIndex = 0;
|
|
||||||
cn1->outNodeId = functionEntryNode->GetId();
|
|
||||||
cn1->outPortIndex = 0;
|
|
||||||
cn1->type = Connection::EXECUTION_LINK;
|
|
||||||
connections.push_back(cn1);
|
|
||||||
|
|
||||||
// Connect variable node to print node (data flow - arg0)
|
|
||||||
auto cn2 = std::make_shared<Connection>();
|
|
||||||
cn2->inNodeId = printNode->GetId();
|
|
||||||
cn2->inPortIndex = 1; // arg0 input port
|
|
||||||
cn2->outNodeId = variableNodeCounter->GetId();
|
|
||||||
cn2->outPortIndex = 0;
|
|
||||||
cn2->type = Connection::DATA_LINK;
|
|
||||||
connections.push_back(cn2);
|
|
||||||
|
|
||||||
// Create generator context with the variables list
|
|
||||||
AssemblyGenerator::GeneratorContext context(
|
|
||||||
variables, // ← IMPORTANT: Pass the variables including counter
|
|
||||||
"2025-01-10 10:00:00",
|
|
||||||
"test-print-args",
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
1024
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create generator
|
|
||||||
AssemblyGeneratorChip32 generator(context);
|
|
||||||
|
|
||||||
// Build AST
|
|
||||||
ASTBuilder builder(nodes, connections);
|
|
||||||
auto pathTree = builder.BuildAST();
|
|
||||||
|
|
||||||
// Generate assembly
|
|
||||||
generator.Reset();
|
|
||||||
generator.GenerateHeader();
|
|
||||||
|
|
||||||
// DATA section - this will now include the counter variable
|
|
||||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
|
||||||
generator.GenerateNodesVariables(nodes); // Print node format string
|
|
||||||
generator.GenerateGlobalVariables(); // ← This generates counter variable
|
|
||||||
|
|
||||||
// TEXT section
|
|
||||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
|
||||||
generator.GenerateTextSection(pathTree);
|
|
||||||
generator.GenerateExit();
|
|
||||||
|
|
||||||
std::string assembly = generator.GetAssembly();
|
|
||||||
|
|
||||||
std::cout << "===== Generated Assembly =====" << std::endl;
|
|
||||||
std::cout << assembly << std::endl;
|
|
||||||
|
|
||||||
// Now the assembly should include the counter variable declaration:
|
|
||||||
// $XEIxIsZoXA DV32, 42 ; counter
|
|
||||||
|
|
||||||
Chip32::Machine machine;
|
|
||||||
machine.QuickExecute(assembly);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Print with multiple arguments") {
|
|
||||||
// ===== Setup =====
|
|
||||||
|
|
||||||
// Variables
|
|
||||||
auto var_a = std::make_shared<Variable>("a");
|
|
||||||
var_a->SetIntegerValue(10); // ← CORRECTION: Utiliser SetIntegerValue() au lieu de SetValue<int>()
|
|
||||||
|
|
||||||
auto var_b = std::make_shared<Variable>("b");
|
|
||||||
var_b->SetIntegerValue(20); // ← CORRECTION: Utiliser SetIntegerValue() au lieu de SetValue<int>()
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Variable>> variables = {var_a, var_b};
|
|
||||||
|
|
||||||
// Nœuds
|
|
||||||
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry-node");
|
|
||||||
functionEntry->SetWeight(100);
|
functionEntry->SetWeight(100);
|
||||||
|
|
||||||
auto varNodeA = std::make_shared<VariableNode>("variable-node");
|
|
||||||
varNodeA->SetVariableUuid(var_a->GetUuid());
|
|
||||||
|
|
||||||
auto varNodeB = std::make_shared<VariableNode>("variable-node");
|
|
||||||
varNodeB->SetVariableUuid(var_b->GetUuid());
|
|
||||||
|
|
||||||
auto addNode = std::make_shared<OperatorNode>("operator-node");
|
|
||||||
addNode->SetOperationType(OperatorNode::OperationType::ADD);
|
|
||||||
|
|
||||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||||
printNode->SetText("Calcul: %d + %d = %d");
|
printNode->SetText("Hello World!");
|
||||||
// IMPORTANT: Appeler Initialize() si nécessaire après SetText()
|
printNode->Initialize();
|
||||||
// printNode->Initialize(); // Si le test ne charge pas depuis JSON
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||||
functionEntry,
|
functionEntry,
|
||||||
varNodeA,
|
|
||||||
varNodeB,
|
|
||||||
addNode,
|
|
||||||
printNode
|
printNode
|
||||||
};
|
};
|
||||||
|
|
||||||
// Connexions
|
// Connections
|
||||||
std::vector<std::shared_ptr<Connection>> connections;
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
// Execution: Entry → Print
|
// Execution: Entry → Print
|
||||||
|
|
@ -150,150 +44,191 @@ TEST_CASE("Print with multiple arguments") {
|
||||||
execConn->type = Connection::EXECUTION_LINK;
|
execConn->type = Connection::EXECUTION_LINK;
|
||||||
connections.push_back(execConn);
|
connections.push_back(execConn);
|
||||||
|
|
||||||
// Data: varA → Print.arg0
|
// Build AST
|
||||||
|
ASTBuilder builder(nodes, connections);
|
||||||
|
auto pathTree = builder.BuildAST();
|
||||||
|
|
||||||
|
// Generate Assembly using TAC
|
||||||
|
AssemblyGenerator::GeneratorContext context(
|
||||||
|
variables,
|
||||||
|
"2025-01-10 10:00:00",
|
||||||
|
"test-print-no-args",
|
||||||
|
true, // debug (will show TAC)
|
||||||
|
true, // optimize
|
||||||
|
1024
|
||||||
|
);
|
||||||
|
|
||||||
|
AssemblyGeneratorChip32TAC tacGen(context);
|
||||||
|
tacGen.GenerateCompleteProgram(nodes, pathTree);
|
||||||
|
|
||||||
|
std::string assembly = tacGen.GetAssembly().str();
|
||||||
|
|
||||||
|
std::cout << "\n--- Generated Assembly ---\n";
|
||||||
|
std::cout << assembly << std::endl;
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
REQUIRE(assembly.find("DATA") != std::string::npos);
|
||||||
|
REQUIRE(assembly.find("CODE") != std::string::npos);
|
||||||
|
REQUIRE(assembly.find("Hello World!") != std::string::npos);
|
||||||
|
REQUIRE(assembly.find("syscall 4") != std::string::npos);
|
||||||
|
REQUIRE(assembly.find("halt") != std::string::npos);
|
||||||
|
|
||||||
|
// Execute
|
||||||
|
std::cout << "\n--- Execution Output ---\n";
|
||||||
|
Chip32::Machine machine;
|
||||||
|
machine.QuickExecute(assembly);
|
||||||
|
|
||||||
|
std::cout << "\n✓ Test passed\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// TEST 2 : Print avec 4 arguments (maximum supporté)
|
||||||
|
// ===================================================================
|
||||||
|
TEST_CASE("Print with 4 arguments - TAC", "[print][args][tac]") {
|
||||||
|
std::cout << "\n=== Test: Print with 4 arguments (TAC) ===\n";
|
||||||
|
|
||||||
|
// Variables: A=10, B=5, C=3, D=2
|
||||||
|
std::vector<std::shared_ptr<Variable>> variables;
|
||||||
|
|
||||||
|
auto var_A = std::make_shared<Variable>("A");
|
||||||
|
var_A->SetIntegerValue(10);
|
||||||
|
variables.push_back(var_A);
|
||||||
|
|
||||||
|
auto var_B = std::make_shared<Variable>("B");
|
||||||
|
var_B->SetIntegerValue(5);
|
||||||
|
variables.push_back(var_B);
|
||||||
|
|
||||||
|
auto var_C = std::make_shared<Variable>("C");
|
||||||
|
var_C->SetIntegerValue(3);
|
||||||
|
variables.push_back(var_C);
|
||||||
|
|
||||||
|
auto var_D = std::make_shared<Variable>("D");
|
||||||
|
var_D->SetIntegerValue(2);
|
||||||
|
variables.push_back(var_D);
|
||||||
|
|
||||||
|
// Nodes
|
||||||
|
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
||||||
|
functionEntry->SetWeight(100);
|
||||||
|
|
||||||
|
auto varNodeA = std::make_shared<VariableNode>("var-node-A");
|
||||||
|
varNodeA->SetVariable(var_A);
|
||||||
|
|
||||||
|
auto varNodeB = std::make_shared<VariableNode>("var-node-B");
|
||||||
|
varNodeB->SetVariable(var_B);
|
||||||
|
|
||||||
|
auto varNodeC = std::make_shared<VariableNode>("var-node-C");
|
||||||
|
varNodeC->SetVariable(var_C);
|
||||||
|
|
||||||
|
auto varNodeD = std::make_shared<VariableNode>("var-node-D");
|
||||||
|
varNodeD->SetVariable(var_D);
|
||||||
|
|
||||||
|
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||||
|
printNode->SetText("Values: A={0}, B={1}, C={2}, D={3}");
|
||||||
|
printNode->Initialize();
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||||
|
functionEntry,
|
||||||
|
varNodeA, varNodeB, varNodeC, varNodeD,
|
||||||
|
printNode
|
||||||
|
};
|
||||||
|
|
||||||
|
// Connections
|
||||||
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
|
// Execution: Entry → Print
|
||||||
|
auto execConn = std::make_shared<Connection>();
|
||||||
|
execConn->outNodeId = functionEntry->GetId();
|
||||||
|
execConn->outPortIndex = 0;
|
||||||
|
execConn->inNodeId = printNode->GetId();
|
||||||
|
execConn->inPortIndex = 0;
|
||||||
|
execConn->type = Connection::EXECUTION_LINK;
|
||||||
|
connections.push_back(execConn);
|
||||||
|
|
||||||
|
// Data: A → Print.arg0 (port 1)
|
||||||
auto dataConn1 = std::make_shared<Connection>();
|
auto dataConn1 = std::make_shared<Connection>();
|
||||||
dataConn1->outNodeId = varNodeA->GetId();
|
dataConn1->outNodeId = varNodeA->GetId();
|
||||||
dataConn1->outPortIndex = 0;
|
dataConn1->outPortIndex = 0;
|
||||||
dataConn1->inNodeId = printNode->GetId();
|
dataConn1->inNodeId = printNode->GetId();
|
||||||
dataConn1->inPortIndex = 1; // ← CORRECTION: arg0 = port 1 (port 0 = execution)
|
dataConn1->inPortIndex = 1;
|
||||||
dataConn1->type = Connection::DATA_LINK;
|
dataConn1->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn1);
|
connections.push_back(dataConn1);
|
||||||
|
|
||||||
// Data: varB → Print.arg1
|
// Data: B → Print.arg1 (port 2)
|
||||||
auto dataConn2 = std::make_shared<Connection>();
|
auto dataConn2 = std::make_shared<Connection>();
|
||||||
dataConn2->outNodeId = varNodeB->GetId();
|
dataConn2->outNodeId = varNodeB->GetId();
|
||||||
dataConn2->outPortIndex = 0;
|
dataConn2->outPortIndex = 0;
|
||||||
dataConn2->inNodeId = printNode->GetId();
|
dataConn2->inNodeId = printNode->GetId();
|
||||||
dataConn2->inPortIndex = 2; // ← CORRECTION: arg1 = port 2
|
dataConn2->inPortIndex = 2;
|
||||||
dataConn2->type = Connection::DATA_LINK;
|
dataConn2->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn2);
|
connections.push_back(dataConn2);
|
||||||
|
|
||||||
// Data: varA → ADD.input0
|
// Data: C → Print.arg2 (port 3)
|
||||||
auto dataConn3 = std::make_shared<Connection>();
|
auto dataConn3 = std::make_shared<Connection>();
|
||||||
dataConn3->outNodeId = varNodeA->GetId();
|
dataConn3->outNodeId = varNodeC->GetId();
|
||||||
dataConn3->outPortIndex = 0;
|
dataConn3->outPortIndex = 0;
|
||||||
dataConn3->inNodeId = addNode->GetId();
|
dataConn3->inNodeId = printNode->GetId();
|
||||||
dataConn3->inPortIndex = 0;
|
dataConn3->inPortIndex = 3;
|
||||||
dataConn3->type = Connection::DATA_LINK;
|
dataConn3->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn3);
|
connections.push_back(dataConn3);
|
||||||
|
|
||||||
// Data: varB → ADD.input1
|
// Data: D → Print.arg3 (port 4)
|
||||||
auto dataConn4 = std::make_shared<Connection>();
|
auto dataConn4 = std::make_shared<Connection>();
|
||||||
dataConn4->outNodeId = varNodeB->GetId();
|
dataConn4->outNodeId = varNodeD->GetId();
|
||||||
dataConn4->outPortIndex = 0;
|
dataConn4->outPortIndex = 0;
|
||||||
dataConn4->inNodeId = addNode->GetId();
|
dataConn4->inNodeId = printNode->GetId();
|
||||||
dataConn4->inPortIndex = 1;
|
dataConn4->inPortIndex = 4;
|
||||||
dataConn4->type = Connection::DATA_LINK;
|
dataConn4->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn4);
|
connections.push_back(dataConn4);
|
||||||
|
|
||||||
// Data: ADD → Print.arg2
|
// Build AST
|
||||||
auto dataConn5 = std::make_shared<Connection>();
|
|
||||||
dataConn5->outNodeId = addNode->GetId();
|
|
||||||
dataConn5->outPortIndex = 0;
|
|
||||||
dataConn5->inNodeId = printNode->GetId();
|
|
||||||
dataConn5->inPortIndex = 3; // ← CORRECTION: arg2 = port 3
|
|
||||||
dataConn5->type = Connection::DATA_LINK;
|
|
||||||
connections.push_back(dataConn5);
|
|
||||||
|
|
||||||
// ===== Build & Generate =====
|
|
||||||
|
|
||||||
ASTBuilder builder(nodes, connections);
|
ASTBuilder builder(nodes, connections);
|
||||||
auto pathTree = builder.BuildAST();
|
auto pathTree = builder.BuildAST();
|
||||||
|
|
||||||
|
// Generate Assembly using TAC
|
||||||
AssemblyGenerator::GeneratorContext context(
|
AssemblyGenerator::GeneratorContext context(
|
||||||
variables,
|
variables,
|
||||||
"2025-01-10 10:00:00",
|
"2025-01-10 10:00:00",
|
||||||
"test-print-multi-args",
|
"test-print-4-args",
|
||||||
true, true, 1024
|
true, // debug (will show TAC)
|
||||||
|
true, // optimize
|
||||||
|
1024
|
||||||
);
|
);
|
||||||
|
|
||||||
AssemblyGeneratorChip32 generator(context);
|
AssemblyGeneratorChip32TAC tacGen(context);
|
||||||
generator.Reset();
|
tacGen.GenerateCompleteProgram(nodes, pathTree);
|
||||||
generator.GenerateHeader();
|
|
||||||
|
|
||||||
|
std::string assembly = tacGen.GetAssembly().str();
|
||||||
|
|
||||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
std::cout << "\n--- Generated Assembly ---\n";
|
||||||
generator.GenerateNodesVariables(nodes);
|
std::cout << assembly << std::endl;
|
||||||
generator.GenerateGlobalVariables();
|
|
||||||
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
|
||||||
generator.GenerateTextSection(pathTree);
|
|
||||||
generator.GenerateExit();
|
|
||||||
|
|
||||||
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
|
// Verify syscall with 4 arguments
|
||||||
// REQUIRE(assembly.find("lcons r1, 3") != std::string::npos);
|
REQUIRE(assembly.find("lcons r1, 4") != std::string::npos);
|
||||||
|
REQUIRE(assembly.find("syscall 4") != std::string::npos);
|
||||||
// // Vérifier chargement des 3 registres
|
|
||||||
// REQUIRE(assembly.find("load r2") != std::string::npos); // arg0
|
|
||||||
// REQUIRE(assembly.find("load r3") != std::string::npos); // arg1
|
|
||||||
// // r4 vient de l'opérateur ADD (devrait être sur la pile ou dans r4)
|
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
|
std::cout << "\n--- Execution Output ---\n";
|
||||||
|
std::cout << "Expected: Values: A=10, B=5, C=3, D=2\n";
|
||||||
|
std::cout << "Actual: ";
|
||||||
|
|
||||||
Chip32::Machine machine;
|
Chip32::Machine machine;
|
||||||
machine.QuickExecute(assembly);
|
machine.QuickExecute(assembly);
|
||||||
}
|
|
||||||
|
std::cout << "\n✓ Test passed\n";
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
@ -9,34 +9,34 @@ Size=400,400
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Library Manager]
|
[Window][Library Manager]
|
||||||
Pos=656,26
|
Pos=630,26
|
||||||
Size=624,313
|
Size=650,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][Console]
|
[Window][Console]
|
||||||
Pos=60,450
|
Pos=60,263
|
||||||
Size=475,270
|
Size=628,457
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,0
|
DockId=0x00000004,0
|
||||||
|
|
||||||
[Window][Emulator]
|
[Window][Emulator]
|
||||||
Pos=656,26
|
Pos=630,26
|
||||||
Size=624,313
|
Size=650,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,5
|
DockId=0x00000002,5
|
||||||
|
|
||||||
[Window][Code viewer]
|
[Window][Code viewer]
|
||||||
Pos=656,26
|
Pos=630,26
|
||||||
Size=624,313
|
Size=650,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,4
|
DockId=0x00000002,4
|
||||||
|
|
||||||
[Window][Resources]
|
[Window][Resources]
|
||||||
Pos=656,26
|
Pos=630,26
|
||||||
Size=624,313
|
Size=650,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,1
|
DockId=0x00000002,1
|
||||||
|
|
||||||
[Window][Node editor]
|
[Window][Node editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
|
|
@ -50,28 +50,28 @@ Size=150,42
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Variables]
|
[Window][Variables]
|
||||||
Pos=537,450
|
Pos=690,263
|
||||||
Size=743,270
|
Size=590,457
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][CPU]
|
[Window][CPU]
|
||||||
Pos=656,26
|
Pos=630,26
|
||||||
Size=624,313
|
Size=650,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,2
|
DockId=0x00000002,2
|
||||||
|
|
||||||
[Window][RAM view]
|
[Window][RAM view]
|
||||||
Pos=656,26
|
Pos=630,26
|
||||||
Size=624,313
|
Size=650,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003,3
|
DockId=0x00000002,3
|
||||||
|
|
||||||
[Window][Properties]
|
[Window][Properties]
|
||||||
Pos=656,341
|
Pos=690,263
|
||||||
Size=624,107
|
Size=590,457
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000006,0
|
DockId=0x00000005,1
|
||||||
|
|
||||||
[Window][ToolBar]
|
[Window][ToolBar]
|
||||||
Pos=0,26
|
Pos=0,26
|
||||||
|
|
@ -90,13 +90,13 @@ Collapsed=0
|
||||||
|
|
||||||
[Window][Module editor]
|
[Window][Module editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=594,422
|
Size=568,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
[Window][Story editor]
|
[Window][Story editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=594,422
|
Size=568,235
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,1
|
DockId=0x00000001,1
|
||||||
|
|
||||||
|
|
@ -106,8 +106,8 @@ Size=687,422
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Error List]
|
[Window][Error List]
|
||||||
Pos=60,450
|
Pos=60,263
|
||||||
Size=475,270
|
Size=628,457
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,1
|
DockId=0x00000004,1
|
||||||
|
|
||||||
|
|
@ -147,14 +147,19 @@ Column 2 Width=120
|
||||||
RefScale=20
|
RefScale=20
|
||||||
Column 0 Sort=0v
|
Column 0 Sort=0v
|
||||||
|
|
||||||
[Docking][Data]
|
[Table][0xED1C9288,4]
|
||||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
|
RefScale=20
|
||||||
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,422 Split=X
|
Column 0 Width=30
|
||||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=790,694 CentralNode=1 Selected=0x93ADCAAB
|
Column 1 Width=80
|
||||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=624,694 Split=Y Selected=0x52EB28B5
|
Column 2 Weight=1.0000
|
||||||
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,476 Selected=0x63869CAF
|
Column 3 Width=60
|
||||||
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,163 Selected=0x8C72BEA8
|
|
||||||
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,270 Split=X Selected=0xEA83D666
|
[Docking][Data]
|
||||||
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=475,192 Selected=0xD246D6BE
|
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
|
||||||
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=743,192 Selected=0x6DE9B20C
|
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,235 Split=X
|
||||||
|
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=1144,694 CentralNode=1 Selected=0x93ADCAAB
|
||||||
|
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=650,694 Selected=0x4B07C626
|
||||||
|
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,457 Split=X Selected=0xEA83D666
|
||||||
|
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=616,192 Selected=0xEA83D666
|
||||||
|
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=578,192 Selected=0x8C72BEA8
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,7 @@ void AppController::CompileNodes(IStoryProject::Type type)
|
||||||
{
|
{
|
||||||
if (type == IStoryProject::Type::PROJECT_TYPE_STORY && m_story)
|
if (type == IStoryProject::Type::PROJECT_TYPE_STORY && m_story)
|
||||||
{
|
{
|
||||||
if (m_story->GenerateScript(m_storyAssembly))
|
if (m_story->GenerateCompleteProgram(m_storyAssembly))
|
||||||
{
|
{
|
||||||
m_logger.Log("Nodes script generated for story.");
|
m_logger.Log("Nodes script generated for story.");
|
||||||
Build(true); // Compile seulement par défaut
|
Build(true); // Compile seulement par défaut
|
||||||
|
|
@ -257,10 +257,10 @@ void AppController::CompileNodes(IStoryProject::Type type)
|
||||||
}
|
}
|
||||||
else if (type == IStoryProject::Type::PROJECT_TYPE_MODULE && m_module)
|
else if (type == IStoryProject::Type::PROJECT_TYPE_MODULE && m_module)
|
||||||
{
|
{
|
||||||
if (m_module->GenerateScript(m_moduleAssembly))
|
if (m_module->GenerateCompleteProgram(m_moduleAssembly))
|
||||||
{
|
{
|
||||||
m_logger.Log("Nodes script generated for module.");
|
m_logger.Log("Nodes script generated for module.");
|
||||||
BuildModule(true); // NEW: Use BuildModule instead
|
BuildModule(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -303,6 +303,12 @@ void AppController::Build(bool compileonly)
|
||||||
{
|
{
|
||||||
m_story->SaveBinary();
|
m_story->SaveBinary();
|
||||||
chip32_initialize(&m_chip32_ctx);
|
chip32_initialize(&m_chip32_ctx);
|
||||||
|
|
||||||
|
Chip32::Instr mainLine;
|
||||||
|
if (m_story->FindMain(mainLine)) {
|
||||||
|
m_chip32_ctx.registers[PC] = mainLine.addr;
|
||||||
|
}
|
||||||
|
|
||||||
m_dbg.run_result = VM_READY;
|
m_dbg.run_result = VM_READY;
|
||||||
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
|
UpdateVmView(); // Notifie la GUI de mettre à jour la vue VM
|
||||||
m_logger.Log("Build successful. VM ready.");
|
m_logger.Log("Build successful. VM ready.");
|
||||||
|
|
@ -349,6 +355,12 @@ void AppController::BuildModule(bool compileonly)
|
||||||
if (m_module->CopyProgramTo(m_rom_data, sizeof(m_rom_data)))
|
if (m_module->CopyProgramTo(m_rom_data, sizeof(m_rom_data)))
|
||||||
{
|
{
|
||||||
chip32_initialize(&m_chip32_ctx);
|
chip32_initialize(&m_chip32_ctx);
|
||||||
|
|
||||||
|
Chip32::Instr mainLine;
|
||||||
|
if (m_module->FindMain(mainLine)) {
|
||||||
|
m_chip32_ctx.registers[PC] = mainLine.addr;
|
||||||
|
}
|
||||||
|
|
||||||
m_dbg.run_result = VM_READY;
|
m_dbg.run_result = VM_READY;
|
||||||
UpdateVmView();
|
UpdateVmView();
|
||||||
|
|
||||||
|
|
@ -396,18 +408,6 @@ void AppController::BuildCode(bool compileonly)
|
||||||
Build(compileonly);
|
Build(compileonly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AppController::CompileNodes(bool compileonly)
|
|
||||||
{
|
|
||||||
if (m_story->GenerateScript(m_storyAssembly))
|
|
||||||
{
|
|
||||||
// m_debuggerWindow.SetScript(m_storyAssembly); // FIXME: GUI event
|
|
||||||
Build(compileonly);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void AppController::SetExternalSourceFile(const std::string &filename)
|
void AppController::SetExternalSourceFile(const std::string &filename)
|
||||||
{
|
{
|
||||||
m_externalSourceFileName = filename;
|
m_externalSourceFileName = filename;
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,6 @@ public:
|
||||||
virtual void Next() override;
|
virtual void Next() override;
|
||||||
virtual void Previous() override;
|
virtual void Previous() override;
|
||||||
virtual std::string VmState() const override;
|
virtual std::string VmState() const override;
|
||||||
virtual void CompileNodes(bool compileonly);
|
|
||||||
virtual void BuildCode(bool compileonly);
|
virtual void BuildCode(bool compileonly);
|
||||||
virtual std::shared_ptr<IStoryProject> GetCurrentProject() override;
|
virtual std::shared_ptr<IStoryProject> GetCurrentProject() override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,11 +99,6 @@ void EmulatorDock::Draw()
|
||||||
// ===== CONTRÔLES DE SCRIPT ET DEBUG =====
|
// ===== CONTRÔLES DE SCRIPT ET DEBUG =====
|
||||||
ImGui::SeparatorText("Script control and debug");
|
ImGui::SeparatorText("Script control and debug");
|
||||||
|
|
||||||
if (ImGui::Button("Build nodes"))
|
|
||||||
{
|
|
||||||
m_story.CompileNodes(true);
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
if (ImGui::Button("Build code"))
|
if (ImGui::Button("Build code"))
|
||||||
{
|
{
|
||||||
m_story.BuildCode(true);
|
m_story.BuildCode(true);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue