mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-13 03:23:24 +01:00
Compare commits
No commits in common. "eb086270296455d669c842f5253ad8397e0bf92b" and "9c6df3c9b68cbe805827b3a3f0fbdbf637af481b" have entirely different histories.
eb08627029
...
9c6df3c9b6
19 changed files with 911 additions and 2790 deletions
36
.github/workflows/vitepress.yml
vendored
Normal file
36
.github/workflows/vitepress.yml
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
name: Deploy-Documentation
|
||||||
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
- run: npm ci
|
||||||
|
working-directory: docs
|
||||||
|
- name: Build
|
||||||
|
run: npm run docs:build
|
||||||
|
working-directory: docs
|
||||||
|
- uses: actions/configure-pages@v2
|
||||||
|
- uses: actions/upload-pages-artifact@v3
|
||||||
|
with:
|
||||||
|
path: docs/.vitepress/dist
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
||||||
|
|
@ -365,7 +365,7 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
|
||||||
{
|
{
|
||||||
if (i.isRamData)
|
if (i.isRamData)
|
||||||
{
|
{
|
||||||
result.ramUsageSize += i.dataLen;
|
result.ramUsageSize += i.dataLen * i.dataTypeSize/8;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -373,10 +373,7 @@ bool Assembler::BuildBinary(std::vector<uint8_t> &program, Result &result)
|
||||||
{
|
{
|
||||||
program.push_back(i.code.opcode);
|
program.push_back(i.code.opcode);
|
||||||
}
|
}
|
||||||
else if (i.isRomData) // Seulement pour ROM DATA
|
result.constantsSize += i.compiledArgs.size();
|
||||||
{
|
|
||||||
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,8 +10,6 @@
|
||||||
#include "chip32_assembler.h"
|
#include "chip32_assembler.h"
|
||||||
#include "chip32_macros.h"
|
#include "chip32_macros.h"
|
||||||
|
|
||||||
// Dans chip32_machine.h
|
|
||||||
|
|
||||||
namespace Chip32
|
namespace Chip32
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -21,207 +19,119 @@ 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 Machine *m_instance;
|
static int get_from_memory(chip32_ctx_t *ctx, uint32_t addr, char *text)
|
||||||
|
|
||||||
Machine() {
|
|
||||||
// Bind syscall handler to this instance
|
|
||||||
m_syscallHandler = std::bind(&Machine::HandleSyscall, this,
|
|
||||||
std::placeholders::_1,
|
|
||||||
std::placeholders::_2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lecture d'une chaîne depuis la mémoire (non statique maintenant)
|
|
||||||
std::string GetStringFromMemory(chip32_ctx_t *ctx, uint32_t addr)
|
|
||||||
{
|
{
|
||||||
if (!ctx) {
|
int valid = 0;
|
||||||
throw std::runtime_error("Invalid context in GetStringFromMemory");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isRam = (addr & 0x80000000) != 0;
|
// Test if address is valid
|
||||||
addr &= 0xFFFF;
|
|
||||||
|
|
||||||
const uint8_t* source_mem = nullptr;
|
|
||||||
size_t mem_size = 0;
|
|
||||||
|
|
||||||
|
bool isRam = addr & 0x80000000;
|
||||||
|
addr &= 0xFFFF; // mask the RAM/ROM bit, ensure 16-bit addressing
|
||||||
if (isRam) {
|
if (isRam) {
|
||||||
if (addr >= ctx->ram.size) {
|
strcpy(&text[0], (const char *)&ctx->ram.mem[addr]);
|
||||||
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 {
|
} else {
|
||||||
if (addr >= ctx->rom.size) {
|
strcpy(&text[0], (const char *)&ctx->rom.mem[addr]);
|
||||||
throw std::out_of_range("ROM address out of bounds: " + std::to_string(addr));
|
|
||||||
}
|
|
||||||
source_mem = ctx->rom.mem;
|
|
||||||
mem_size = ctx->rom.size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t max_len = mem_size - addr;
|
return valid;
|
||||||
const char* str_start = reinterpret_cast<const char*>(&source_mem[addr]);
|
|
||||||
|
|
||||||
const char* null_pos = static_cast<const char*>(
|
|
||||||
std::memchr(str_start, '\0', max_len)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (null_pos) {
|
|
||||||
return std::string(str_start, null_pos - str_start);
|
|
||||||
} else {
|
|
||||||
return std::string(str_start, max_len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formatter avec nombre variable d'arguments (non statique)
|
|
||||||
std::string FormatString(const std::string& format,
|
|
||||||
const std::vector<uint32_t>& args)
|
static uint8_t story_player_syscall(chip32_ctx_t *ctx, uint8_t code)
|
||||||
{
|
{
|
||||||
if (args.empty()) {
|
uint8_t retCode = SYSCALL_RET_OK;
|
||||||
return format;
|
char working_buf[100] = {0};
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> buffer(1024);
|
// Printf
|
||||||
int result = -1;
|
if (code == 4)
|
||||||
|
{
|
||||||
|
// In R0: string with escaped characters
|
||||||
|
// R1: Number of arguments
|
||||||
|
// R2, R3 ... arguments
|
||||||
|
|
||||||
|
// Integers: stored in registers by values
|
||||||
|
// Strings: first character address in register
|
||||||
|
|
||||||
|
get_from_memory(ctx, ctx->registers[R0], working_buf);
|
||||||
|
int arg_count = ctx->registers[R1];
|
||||||
|
|
||||||
|
switch(arg_count){
|
||||||
|
case 0:
|
||||||
|
puts(working_buf);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
printf(working_buf, ctx->registers[R2]);
|
||||||
|
puts("");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
printf(working_buf, ctx->registers[R2], ctx->registers[R3]);
|
||||||
|
puts("");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
printf(working_buf, ctx->registers[R2], ctx->registers[R3], ctx->registers[R4]);
|
||||||
|
puts("");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (args.size()) {
|
}
|
||||||
case 1:
|
// WAIT (sleep)
|
||||||
result = std::snprintf(buffer.data(), buffer.size(),
|
else if (code == 5)
|
||||||
format.c_str(), args[0]);
|
{
|
||||||
break;
|
std::this_thread::sleep_for(std::chrono::milliseconds(ctx->registers[R0]));
|
||||||
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) {
|
return retCode;
|
||||||
throw std::runtime_error("Error formatting string");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (static_cast<size_t>(result) >= buffer.size()) {
|
|
||||||
buffer.resize(result + 1);
|
|
||||||
|
|
||||||
switch (args.size()) {
|
|
||||||
case 1:
|
|
||||||
std::snprintf(buffer.data(), buffer.size(),
|
|
||||||
format.c_str(), args[0]);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
std::snprintf(buffer.data(), buffer.size(),
|
|
||||||
format.c_str(), args[0], args[1]);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
std::snprintf(buffer.data(), buffer.size(),
|
|
||||||
format.c_str(), args[0], args[1], args[2]);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
std::snprintf(buffer.data(), buffer.size(),
|
|
||||||
format.c_str(), args[0], args[1], args[2], args[3]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
std::vector<uint8_t> data(8*1024);
|
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();
|
|
||||||
|
|
||||||
// Utiliser le wrapper statique qui appelle notre fonction membre
|
chip32_ctx.ram.mem = data;
|
||||||
chip32_ctx.syscall = SyscallWrapper;
|
chip32_ctx.ram.addr = 40 *1024;
|
||||||
chip32_ctx.user_data = this; // Stocker le pointeur vers cette instance
|
chip32_ctx.ram.size = sizeof(data);
|
||||||
|
|
||||||
|
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,7 +213,6 @@ 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,6 +43,7 @@ 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;
|
||||||
|
|
|
||||||
509
core/story-manager/src/compiler/assembly_generator_chip32.h
Normal file
509
core/story-manager/src/compiler/assembly_generator_chip32.h
Normal file
|
|
@ -0,0 +1,509 @@
|
||||||
|
#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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,621 +0,0 @@
|
||||||
// ===================================================================
|
|
||||||
// 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,19 +183,14 @@ 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];
|
||||||
|
|
||||||
// Ajouter TOUTES les connexions de données, pas seulement celles depuis VariableNode
|
// Keep variables nodes as data inputs
|
||||||
inNode->AddDataInput(conn->inPortIndex, outNode);
|
if (dynamic_cast<VariableNode*>(outNode->node.get()))
|
||||||
|
{
|
||||||
// Ajouter aussi dans le sens inverse pour les dataOutputs
|
inNode->AddDataInput(conn->inPortIndex, outNode);
|
||||||
outNode->AddDataOutput(conn->outPortIndex, inNode, conn->inPortIndex);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build execution paths
|
// Build execution paths
|
||||||
|
|
|
||||||
|
|
@ -1,706 +0,0 @@
|
||||||
// ===================================================================
|
|
||||||
// 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,7 +55,21 @@ 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_tac.h"
|
#include "assembly_generator_chip32.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); // FIXME
|
GenerateScript(code);
|
||||||
|
|
||||||
std::cout << code << std::endl;
|
std::cout << code << std::endl;
|
||||||
|
|
||||||
|
|
@ -411,109 +411,63 @@ 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)
|
|
||||||
{
|
{
|
||||||
// === PHASE 1 : COLLECTE ===
|
bool retCode = true;
|
||||||
// Collecter tous les nœuds et connexions de toutes les pages
|
std::stringstream code;
|
||||||
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;
|
|
||||||
|
|
||||||
for (const auto& page : m_pages) {
|
// Empty resources usage
|
||||||
auto [nodesBegin, nodesEnd] = page->Nodes();
|
m_usedLabels.clear();
|
||||||
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());
|
|
||||||
|
|
||||||
// Stocker les données de chaque page
|
|
||||||
pageData[std::string(page->Uuid())] = {pageNodes, pageLinks};
|
|
||||||
}
|
|
||||||
|
|
||||||
// === PHASE 2 : GÉNÉRATION ===
|
|
||||||
AssemblyGenerator::GeneratorContext context(
|
|
||||||
m_variables,
|
|
||||||
"2025-01-10 15:30:00",
|
|
||||||
"story-project",
|
|
||||||
true, // debug
|
|
||||||
true, // optimize
|
|
||||||
1024
|
|
||||||
);
|
|
||||||
|
|
||||||
AssemblyGeneratorChip32TAC generator(context);
|
|
||||||
|
|
||||||
// Header
|
|
||||||
generator.Reset();
|
|
||||||
generator.GenerateHeader();
|
|
||||||
|
|
||||||
// === SECTION DATA (commune à toutes les pages) ===
|
|
||||||
generator.StartSection(AssemblyGenerator::Section::DATA);
|
|
||||||
|
|
||||||
// Variables globales (partagées entre toutes les pages)
|
|
||||||
generator.GenerateGlobalVariables();
|
|
||||||
|
|
||||||
// Constantes de tous les nœuds de toutes les pages
|
|
||||||
generator.GenerateNodesVariables(allNodes);
|
|
||||||
|
|
||||||
// === SECTION TEXT (chaque page = une fonction) ===
|
|
||||||
generator.AddComment("======================= CODE =======================");
|
|
||||||
|
|
||||||
// Générer chaque page comme une fonction
|
|
||||||
bool isFirstPage = true;
|
|
||||||
for (const auto& page : m_pages) {
|
|
||||||
std::string pageUuid(page->Uuid());
|
|
||||||
auto& [nodes, connections] = pageData[pageUuid];
|
|
||||||
|
|
||||||
// Construire l'AST pour cette page
|
|
||||||
ASTBuilder builder(nodes, connections);
|
|
||||||
auto astNodes = builder.BuildAST();
|
|
||||||
|
|
||||||
// Générer le label de fonction
|
|
||||||
std::string functionLabel;
|
|
||||||
if (isFirstPage || pageUuid == MainUuid()) {
|
|
||||||
functionLabel = ".main";
|
|
||||||
isFirstPage = false;
|
|
||||||
} else {
|
|
||||||
functionLabel = ".page_" + pageUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
generator.AddComment("========================================");
|
|
||||||
generator.AddComment("Page: " + std::string(page->GetName()));
|
|
||||||
generator.AddComment("UUID: " + pageUuid);
|
|
||||||
generator.AddComment("========================================");
|
|
||||||
generator.GetAssembly() << functionLabel << ":\n";
|
|
||||||
|
|
||||||
// Générer le TAC pour cette page
|
|
||||||
TACGenerator tacGen;
|
|
||||||
TACProgram pageTAC = tacGen.Generate(astNodes);
|
|
||||||
|
|
||||||
if (context.debugOutput) {
|
|
||||||
std::cout << "\n=== TAC for page: " << page->GetName() << " ===\n";
|
|
||||||
std::cout << pageTAC.ToString() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convertir le TAC en assembleur
|
|
||||||
generator.GenerateTACToAssembly(pageTAC);
|
|
||||||
|
|
||||||
// Retour de la fonction (sauf pour main)
|
|
||||||
if (functionLabel != ".main") {
|
|
||||||
generator.GetAssembly() << " ret\n\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit du programme
|
|
||||||
generator.GenerateExit();
|
|
||||||
|
|
||||||
assembly = generator.GetAssembly().str();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
|
// Create generator context with current time and user
|
||||||
|
AssemblyGenerator::GeneratorContext context(
|
||||||
|
m_variables,
|
||||||
|
"2025-04-08 12:09:01", // Current UTC time
|
||||||
|
"story-editor", // Current user
|
||||||
|
true, // Enable debug output
|
||||||
|
true, // Enable optimizations
|
||||||
|
1024 // Stack size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create generator
|
||||||
|
AssemblyGeneratorChip32 generator(context);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
generator.Reset();
|
||||||
|
|
||||||
|
// Generate header comments
|
||||||
|
generator.GenerateHeader();
|
||||||
|
|
||||||
|
// Generate text section
|
||||||
|
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||||
|
for (const auto & p : m_pages)
|
||||||
|
{
|
||||||
|
p->CompileNodes(generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate data section
|
||||||
|
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||||
|
for (const auto & p : m_pages)
|
||||||
|
{
|
||||||
|
p->CompileNodesVariables(generator);
|
||||||
|
}
|
||||||
|
generator.GenerateGlobalVariables();
|
||||||
|
|
||||||
|
generator.GenerateExit();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
m_log.Log(e.what(), true);
|
||||||
|
retCode = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
codeStr = generator.GetAssembly();
|
||||||
|
|
||||||
|
return retCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
|
bool StoryProject::GenerateBinary(const std::string &code, Chip32::Assembler::Error &err)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
#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;
|
||||||
|
|
||||||
|
|
@ -39,14 +38,9 @@ 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,4 +1,6 @@
|
||||||
// test_print_node.cpp
|
// test_print_with_args.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"
|
||||||
|
|
@ -6,133 +8,137 @@
|
||||||
#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_tac.h"
|
#include "assembly_generator_chip32.h"
|
||||||
#include "chip32_machine.h"
|
#include "chip32_machine.h"
|
||||||
#include "variable.h"
|
#include "variable.h"
|
||||||
|
|
||||||
// ===================================================================
|
TEST_CASE("Print with single argument") {
|
||||||
// TEST 1 : Print simple sans argument
|
// Create the print node
|
||||||
// ===================================================================
|
|
||||||
TEST_CASE("Print without arguments - TAC", "[print][simple][tac]") {
|
|
||||||
std::cout << "\n=== Test: Print without arguments (TAC) ===\n";
|
|
||||||
|
|
||||||
// Variables (vide pour ce test)
|
|
||||||
std::vector<std::shared_ptr<Variable>> variables;
|
|
||||||
|
|
||||||
// Nodes
|
|
||||||
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry");
|
|
||||||
functionEntry->SetWeight(100);
|
|
||||||
|
|
||||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||||
printNode->SetText("Hello World!");
|
printNode->SetText("Compteur: %d");
|
||||||
printNode->Initialize();
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
// Create function entry
|
||||||
functionEntry,
|
auto functionEntryNode = std::make_shared<FunctionEntryNode>("function-entry-node");
|
||||||
printNode
|
|
||||||
};
|
|
||||||
|
|
||||||
// Connections
|
// IMPORTANT: Create the "counter" variable and add it to the global variables list
|
||||||
|
std::vector<std::shared_ptr<Variable>> variables;
|
||||||
|
auto counterVar = std::make_shared<Variable>("counter");
|
||||||
|
counterVar->SetIntegerValue(42); // Initial value
|
||||||
|
variables.push_back(counterVar); // ← CRUCIAL: Add to global variables
|
||||||
|
|
||||||
|
// Create a variable node that references the counter variable
|
||||||
|
auto variableNodeCounter = std::make_shared<VariableNode>("variable-node");
|
||||||
|
variableNodeCounter->SetVariable(counterVar);
|
||||||
|
|
||||||
|
// Build the node list
|
||||||
|
std::vector<std::shared_ptr<BaseNode>> nodes;
|
||||||
|
nodes.push_back(functionEntryNode);
|
||||||
|
nodes.push_back(printNode);
|
||||||
|
nodes.push_back(variableNodeCounter);
|
||||||
|
|
||||||
|
// Create connections
|
||||||
std::vector<std::shared_ptr<Connection>> connections;
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
// Execution: Entry → Print
|
// Connect function entry to print node (execution flow)
|
||||||
auto execConn = std::make_shared<Connection>();
|
auto cn1 = std::make_shared<Connection>();
|
||||||
execConn->outNodeId = functionEntry->GetId();
|
cn1->inNodeId = printNode->GetId();
|
||||||
execConn->outPortIndex = 0;
|
cn1->inPortIndex = 0;
|
||||||
execConn->inNodeId = printNode->GetId();
|
cn1->outNodeId = functionEntryNode->GetId();
|
||||||
execConn->inPortIndex = 0;
|
cn1->outPortIndex = 0;
|
||||||
execConn->type = Connection::EXECUTION_LINK;
|
cn1->type = Connection::EXECUTION_LINK;
|
||||||
connections.push_back(execConn);
|
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
|
// Build AST
|
||||||
ASTBuilder builder(nodes, connections);
|
ASTBuilder builder(nodes, connections);
|
||||||
auto pathTree = builder.BuildAST();
|
auto pathTree = builder.BuildAST();
|
||||||
|
|
||||||
// Generate Assembly using TAC
|
// Generate assembly
|
||||||
AssemblyGenerator::GeneratorContext context(
|
generator.Reset();
|
||||||
variables,
|
generator.GenerateHeader();
|
||||||
"2025-01-10 10:00:00",
|
|
||||||
"test-print-no-args",
|
|
||||||
true, // debug (will show TAC)
|
|
||||||
true, // optimize
|
|
||||||
1024
|
|
||||||
);
|
|
||||||
|
|
||||||
AssemblyGeneratorChip32TAC tacGen(context);
|
// DATA section - this will now include the counter variable
|
||||||
tacGen.GenerateCompleteProgram(nodes, pathTree);
|
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||||
|
generator.GenerateNodesVariables(nodes); // Print node format string
|
||||||
|
generator.GenerateGlobalVariables(); // ← This generates counter variable
|
||||||
|
|
||||||
std::string assembly = tacGen.GetAssembly().str();
|
// TEXT section
|
||||||
|
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||||
|
generator.GenerateTextSection(pathTree);
|
||||||
|
generator.GenerateExit();
|
||||||
|
|
||||||
std::cout << "\n--- Generated Assembly ---\n";
|
std::string assembly = generator.GetAssembly();
|
||||||
|
|
||||||
|
std::cout << "===== Generated Assembly =====" << std::endl;
|
||||||
std::cout << assembly << std::endl;
|
std::cout << assembly << std::endl;
|
||||||
|
|
||||||
// Verify
|
// Now the assembly should include the counter variable declaration:
|
||||||
REQUIRE(assembly.find("DATA") != std::string::npos);
|
// $XEIxIsZoXA DV32, 42 ; counter
|
||||||
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;
|
Chip32::Machine machine;
|
||||||
machine.QuickExecute(assembly);
|
machine.QuickExecute(assembly);
|
||||||
|
|
||||||
std::cout << "\n✓ Test passed\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===================================================================
|
TEST_CASE("Print with multiple arguments") {
|
||||||
// TEST 2 : Print avec 4 arguments (maximum supporté)
|
// ===== Setup =====
|
||||||
// ===================================================================
|
|
||||||
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
|
// Variables
|
||||||
std::vector<std::shared_ptr<Variable>> variables;
|
auto var_a = std::make_shared<Variable>("a");
|
||||||
|
var_a->SetIntegerValue(10); // ← CORRECTION: Utiliser SetIntegerValue() au lieu de SetValue<int>()
|
||||||
|
|
||||||
auto var_A = std::make_shared<Variable>("A");
|
auto var_b = std::make_shared<Variable>("b");
|
||||||
var_A->SetIntegerValue(10);
|
var_b->SetIntegerValue(20); // ← CORRECTION: Utiliser SetIntegerValue() au lieu de SetValue<int>()
|
||||||
variables.push_back(var_A);
|
|
||||||
|
|
||||||
auto var_B = std::make_shared<Variable>("B");
|
std::vector<std::shared_ptr<Variable>> variables = {var_a, var_b};
|
||||||
var_B->SetIntegerValue(5);
|
|
||||||
variables.push_back(var_B);
|
|
||||||
|
|
||||||
auto var_C = std::make_shared<Variable>("C");
|
// Nœuds
|
||||||
var_C->SetIntegerValue(3);
|
auto functionEntry = std::make_shared<FunctionEntryNode>("function-entry-node");
|
||||||
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);
|
functionEntry->SetWeight(100);
|
||||||
|
|
||||||
auto varNodeA = std::make_shared<VariableNode>("var-node-A");
|
auto varNodeA = std::make_shared<VariableNode>("variable-node");
|
||||||
varNodeA->SetVariable(var_A);
|
varNodeA->SetVariableUuid(var_a->GetUuid());
|
||||||
|
|
||||||
auto varNodeB = std::make_shared<VariableNode>("var-node-B");
|
auto varNodeB = std::make_shared<VariableNode>("variable-node");
|
||||||
varNodeB->SetVariable(var_B);
|
varNodeB->SetVariableUuid(var_b->GetUuid());
|
||||||
|
|
||||||
auto varNodeC = std::make_shared<VariableNode>("var-node-C");
|
auto addNode = std::make_shared<OperatorNode>("operator-node");
|
||||||
varNodeC->SetVariable(var_C);
|
addNode->SetOperationType(OperatorNode::OperationType::ADD);
|
||||||
|
|
||||||
auto varNodeD = std::make_shared<VariableNode>("var-node-D");
|
|
||||||
varNodeD->SetVariable(var_D);
|
|
||||||
|
|
||||||
auto printNode = std::make_shared<PrintNode>("print-node");
|
auto printNode = std::make_shared<PrintNode>("print-node");
|
||||||
printNode->SetText("Values: A={0}, B={1}, C={2}, D={3}");
|
printNode->SetText("Calcul: %d + %d = %d");
|
||||||
printNode->Initialize();
|
// IMPORTANT: Appeler Initialize() si nécessaire après SetText()
|
||||||
|
// printNode->Initialize(); // Si le test ne charge pas depuis JSON
|
||||||
|
|
||||||
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
std::vector<std::shared_ptr<BaseNode>> nodes = {
|
||||||
functionEntry,
|
functionEntry,
|
||||||
varNodeA, varNodeB, varNodeC, varNodeD,
|
varNodeA,
|
||||||
|
varNodeB,
|
||||||
|
addNode,
|
||||||
printNode
|
printNode
|
||||||
};
|
};
|
||||||
|
|
||||||
// Connections
|
// Connexions
|
||||||
std::vector<std::shared_ptr<Connection>> connections;
|
std::vector<std::shared_ptr<Connection>> connections;
|
||||||
|
|
||||||
// Execution: Entry → Print
|
// Execution: Entry → Print
|
||||||
|
|
@ -144,91 +150,90 @@ TEST_CASE("Print with 4 arguments - TAC", "[print][args][tac]") {
|
||||||
execConn->type = Connection::EXECUTION_LINK;
|
execConn->type = Connection::EXECUTION_LINK;
|
||||||
connections.push_back(execConn);
|
connections.push_back(execConn);
|
||||||
|
|
||||||
// Data: A → Print.arg0 (port 1)
|
// Data: varA → Print.arg0
|
||||||
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;
|
dataConn1->inPortIndex = 1; // ← CORRECTION: arg0 = port 1 (port 0 = execution)
|
||||||
dataConn1->type = Connection::DATA_LINK;
|
dataConn1->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn1);
|
connections.push_back(dataConn1);
|
||||||
|
|
||||||
// Data: B → Print.arg1 (port 2)
|
// Data: varB → Print.arg1
|
||||||
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;
|
dataConn2->inPortIndex = 2; // ← CORRECTION: arg1 = port 2
|
||||||
dataConn2->type = Connection::DATA_LINK;
|
dataConn2->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn2);
|
connections.push_back(dataConn2);
|
||||||
|
|
||||||
// Data: C → Print.arg2 (port 3)
|
// Data: varA → ADD.input0
|
||||||
auto dataConn3 = std::make_shared<Connection>();
|
auto dataConn3 = std::make_shared<Connection>();
|
||||||
dataConn3->outNodeId = varNodeC->GetId();
|
dataConn3->outNodeId = varNodeA->GetId();
|
||||||
dataConn3->outPortIndex = 0;
|
dataConn3->outPortIndex = 0;
|
||||||
dataConn3->inNodeId = printNode->GetId();
|
dataConn3->inNodeId = addNode->GetId();
|
||||||
dataConn3->inPortIndex = 3;
|
dataConn3->inPortIndex = 0;
|
||||||
dataConn3->type = Connection::DATA_LINK;
|
dataConn3->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn3);
|
connections.push_back(dataConn3);
|
||||||
|
|
||||||
// Data: D → Print.arg3 (port 4)
|
// Data: varB → ADD.input1
|
||||||
auto dataConn4 = std::make_shared<Connection>();
|
auto dataConn4 = std::make_shared<Connection>();
|
||||||
dataConn4->outNodeId = varNodeD->GetId();
|
dataConn4->outNodeId = varNodeB->GetId();
|
||||||
dataConn4->outPortIndex = 0;
|
dataConn4->outPortIndex = 0;
|
||||||
dataConn4->inNodeId = printNode->GetId();
|
dataConn4->inNodeId = addNode->GetId();
|
||||||
dataConn4->inPortIndex = 4;
|
dataConn4->inPortIndex = 1;
|
||||||
dataConn4->type = Connection::DATA_LINK;
|
dataConn4->type = Connection::DATA_LINK;
|
||||||
connections.push_back(dataConn4);
|
connections.push_back(dataConn4);
|
||||||
|
|
||||||
// Build AST
|
// Data: ADD → Print.arg2
|
||||||
|
auto dataConn5 = std::make_shared<Connection>();
|
||||||
|
dataConn5->outNodeId = addNode->GetId();
|
||||||
|
dataConn5->outPortIndex = 0;
|
||||||
|
dataConn5->inNodeId = printNode->GetId();
|
||||||
|
dataConn5->inPortIndex = 3; // ← CORRECTION: arg2 = port 3
|
||||||
|
dataConn5->type = Connection::DATA_LINK;
|
||||||
|
connections.push_back(dataConn5);
|
||||||
|
|
||||||
|
// ===== Build & Generate =====
|
||||||
|
|
||||||
ASTBuilder builder(nodes, connections);
|
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-4-args",
|
"test-print-multi-args",
|
||||||
true, // debug (will show TAC)
|
true, true, 1024
|
||||||
true, // optimize
|
|
||||||
1024
|
|
||||||
);
|
);
|
||||||
|
|
||||||
AssemblyGeneratorChip32TAC tacGen(context);
|
AssemblyGeneratorChip32 generator(context);
|
||||||
tacGen.GenerateCompleteProgram(nodes, pathTree);
|
generator.Reset();
|
||||||
|
generator.GenerateHeader();
|
||||||
|
|
||||||
|
|
||||||
std::string assembly = tacGen.GetAssembly().str();
|
generator.StartSection(AssemblyGenerator::Section::DATA);
|
||||||
|
generator.GenerateNodesVariables(nodes);
|
||||||
|
generator.GenerateGlobalVariables();
|
||||||
|
generator.StartSection(AssemblyGenerator::Section::TEXT);
|
||||||
|
generator.GenerateTextSection(pathTree);
|
||||||
|
generator.GenerateExit();
|
||||||
|
|
||||||
std::cout << "\n--- Generated Assembly ---\n";
|
std::string assembly = generator.GetAssembly();
|
||||||
std::cout << assembly << std::endl;
|
|
||||||
|
|
||||||
// Verify
|
std::cout << "\n===== Generated Assembly (Multi-Args) =====\n" << assembly << std::endl;
|
||||||
REQUIRE(assembly.find("DATA") != std::string::npos);
|
|
||||||
REQUIRE(assembly.find("CODE") != std::string::npos);
|
|
||||||
|
|
||||||
// Verify variable declarations
|
// ===== Vérifications =====
|
||||||
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);
|
|
||||||
|
|
||||||
// Verify format string conversion {0} → %d
|
// // Vérifier 3 arguments
|
||||||
REQUIRE(assembly.find("{0}") == std::string::npos);
|
// REQUIRE(assembly.find("lcons r1, 3") != std::string::npos);
|
||||||
REQUIRE(assembly.find("{1}") == std::string::npos);
|
|
||||||
REQUIRE(assembly.find("{2}") == std::string::npos);
|
|
||||||
REQUIRE(assembly.find("{3}") == std::string::npos);
|
|
||||||
|
|
||||||
// Verify syscall with 4 arguments
|
// // Vérifier chargement des 3 registres
|
||||||
REQUIRE(assembly.find("lcons r1, 4") != std::string::npos);
|
// REQUIRE(assembly.find("load r2") != std::string::npos); // arg0
|
||||||
REQUIRE(assembly.find("syscall 4") != std::string::npos);
|
// 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";
|
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,7 @@ add_compile_definitions(CUSTOM_IMGUIFILEDIALOG_CONFIG="${CMAKE_SOURCE_DIR}/src/C
|
||||||
add_compile_definitions(IMGUI_INCLUDE="imgui.h")
|
add_compile_definitions(IMGUI_INCLUDE="imgui.h")
|
||||||
add_subdirectory(libs/ImGuiFileDialog)
|
add_subdirectory(libs/ImGuiFileDialog)
|
||||||
|
|
||||||
include_directories(externals/ImNodeFlow/src externals/ImNodeFlow/include)
|
include_directories(externals/ImNodeFlow/src)
|
||||||
|
|
||||||
add_subdirectory(externals/ImNodeFlow EXCLUDE_FROM_ALL)
|
add_subdirectory(externals/ImNodeFlow EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,34 +9,34 @@ Size=400,400
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Library Manager]
|
[Window][Library Manager]
|
||||||
Pos=630,26
|
Pos=656,26
|
||||||
Size=650,235
|
Size=624,313
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000003,0
|
||||||
|
|
||||||
[Window][Console]
|
[Window][Console]
|
||||||
Pos=60,263
|
Pos=60,450
|
||||||
Size=628,457
|
Size=475,270
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,0
|
DockId=0x00000004,0
|
||||||
|
|
||||||
[Window][Emulator]
|
[Window][Emulator]
|
||||||
Pos=630,26
|
Pos=656,26
|
||||||
Size=650,235
|
Size=624,313
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,5
|
DockId=0x00000003,5
|
||||||
|
|
||||||
[Window][Code viewer]
|
[Window][Code viewer]
|
||||||
Pos=630,26
|
Pos=656,26
|
||||||
Size=650,235
|
Size=624,313
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,4
|
DockId=0x00000003,4
|
||||||
|
|
||||||
[Window][Resources]
|
[Window][Resources]
|
||||||
Pos=630,26
|
Pos=656,26
|
||||||
Size=650,235
|
Size=624,313
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000003,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=690,263
|
Pos=537,450
|
||||||
Size=590,457
|
Size=743,270
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][CPU]
|
[Window][CPU]
|
||||||
Pos=630,26
|
Pos=656,26
|
||||||
Size=650,235
|
Size=624,313
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,2
|
DockId=0x00000003,2
|
||||||
|
|
||||||
[Window][RAM view]
|
[Window][RAM view]
|
||||||
Pos=630,26
|
Pos=656,26
|
||||||
Size=650,235
|
Size=624,313
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,3
|
DockId=0x00000003,3
|
||||||
|
|
||||||
[Window][Properties]
|
[Window][Properties]
|
||||||
Pos=690,263
|
Pos=656,341
|
||||||
Size=590,457
|
Size=624,107
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,1
|
DockId=0x00000006,0
|
||||||
|
|
||||||
[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=568,235
|
Size=594,422
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
[Window][Story editor]
|
[Window][Story editor]
|
||||||
Pos=60,26
|
Pos=60,26
|
||||||
Size=568,235
|
Size=594,422
|
||||||
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,263
|
Pos=60,450
|
||||||
Size=628,457
|
Size=475,270
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004,1
|
DockId=0x00000004,1
|
||||||
|
|
||||||
|
|
@ -147,19 +147,14 @@ Column 2 Width=120
|
||||||
RefScale=20
|
RefScale=20
|
||||||
Column 0 Sort=0v
|
Column 0 Sort=0v
|
||||||
|
|
||||||
[Table][0xED1C9288,4]
|
|
||||||
RefScale=20
|
|
||||||
Column 0 Width=30
|
|
||||||
Column 1 Width=80
|
|
||||||
Column 2 Weight=1.0000
|
|
||||||
Column 3 Width=60
|
|
||||||
|
|
||||||
[Docking][Data]
|
[Docking][Data]
|
||||||
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
|
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y
|
||||||
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,235 Split=X
|
DockNode ID=0x00000007 Parent=0x08BD597D SizeRef=1220,422 Split=X
|
||||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=1144,694 CentralNode=1 Selected=0x93ADCAAB
|
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=790,694 CentralNode=1 Selected=0x93ADCAAB
|
||||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=650,694 Selected=0x4B07C626
|
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=624,694 Split=Y Selected=0x52EB28B5
|
||||||
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,457 Split=X Selected=0xEA83D666
|
DockNode ID=0x00000003 Parent=0x00000002 SizeRef=718,476 Selected=0x63869CAF
|
||||||
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=616,192 Selected=0xEA83D666
|
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=718,163 Selected=0x8C72BEA8
|
||||||
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=578,192 Selected=0x8C72BEA8
|
DockNode ID=0x00000008 Parent=0x08BD597D SizeRef=1220,270 Split=X Selected=0xEA83D666
|
||||||
|
DockNode ID=0x00000004 Parent=0x00000008 SizeRef=475,192 Selected=0xD246D6BE
|
||||||
|
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=743,192 Selected=0x6DE9B20C
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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->GenerateCompleteProgram(m_storyAssembly))
|
if (m_story->GenerateScript(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->GenerateCompleteProgram(m_moduleAssembly))
|
if (m_module->GenerateScript(m_moduleAssembly))
|
||||||
{
|
{
|
||||||
m_logger.Log("Nodes script generated for module.");
|
m_logger.Log("Nodes script generated for module.");
|
||||||
BuildModule(true);
|
BuildModule(true); // NEW: Use BuildModule instead
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -303,12 +303,6 @@ 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.");
|
||||||
|
|
@ -355,12 +349,6 @@ 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();
|
||||||
|
|
||||||
|
|
@ -408,6 +396,18 @@ 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,6 +87,7 @@ 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,6 +99,11 @@ 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