Skip to content

Commit

Permalink
feat(REPL): first (almost) working version of the JIT REPL
Browse files Browse the repository at this point in the history
  • Loading branch information
wesuRage committed Jan 18, 2025
1 parent f7f961d commit 7e186d4
Showing 1 changed file with 62 additions and 154 deletions.
216 changes: 62 additions & 154 deletions src/backend/generator/generator.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,195 +45,103 @@ extern "C" {
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
#include <llvm/Support/TargetSelect.h>

// Função para adicionar o código de entrada (entry.main) e executar
void createMainFunc(llvm::Module &module, llvm::LLVMContext &context, llvm::IRBuilder<llvm::NoFolder> &builder) {
llvm::FunctionType *mainFuncType = llvm::FunctionType::get(builder.getVoidTy(), false);
llvm::Function *mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, "entry.main", module);

llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, "entry", mainFunc);
builder.SetInsertPoint(entryBlock);

FILE *outFile = fopen("repl.glx", "r");

int count = 0;
Token *tokenArray = tokenize(outFile, "repl.glx", &count); // Passando FILE*, nome do arquivo e count

// Gerar AST com base nos tokens
Parser parser = parser_new();
AstNode *ast = produce_ast(&parser, tokenArray, count, true); // Função fornecida por você
if (!ast) {
std::cerr << "Error: Invalid input or failed to parse AST.\n";
}

generate_ir(ast, context, module, builder);

// Adicionar código gerado ao JIT
builder.CreateRetVoid();
}

// Função para processar os nós AST
void processAstNode(AstNode *node, std::string &Line, FILE *outFile, std::stack<std::string> &declarationStack, std::vector<std::string>& codeLines) {
if (node == nullptr) {
std::cerr << "Error: Encountered null node!" << std::endl;
return;
}

// Verificação usando switch para os tipos de nós
switch (node->kind) {
case NODE_EXTERN:
case NODE_FUNCTION:
case NODE_VARIABLE:
case NODE_ASSIGNMENT:
// Se for uma função, variável ou atribuição, empilhar a linha
declarationStack.push(Line);
// Armazenar o código no vetor para mais tarde
codeLines.push_back(Line);
break;
default:
// Se não for declaração nem atribuição, processa normalmente
break;
}
}

// Função para concatenar as declarações e escrevê-las no arquivo
void writeDeclarationsToFile(std::stack<std::string> &declarationStack, FILE *outFile) {
std::vector<std::string> declarations; // Vetor para armazenar as declarações

// Empilhar as declarações no vetor
while (!declarationStack.empty()) {
declarations.push_back(declarationStack.top());
declarationStack.pop();
}

// Concatenar as declarações em uma única string
std::string allDeclarations;
for (const auto& decl : declarations) {
allDeclarations += decl + "\n"; // Adiciona um newline para separar as declarações
}

// Escrever a string concatenada no arquivo
fprintf(outFile, "%s", allDeclarations.c_str());
}

// Função principal do REPL
int startREPL() {
// Inicializar o LLVM e o JIT
// Inicializar LLVM
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();

std::unique_ptr<llvm::LLVMContext> Context = std::make_unique<llvm::LLVMContext>();
// Criar o JIT fora do loop
auto JIT = llvm::orc::LLJITBuilder().create();
if (!JIT) {
llvm::errs() << "Failed to initialize LLJIT: " << llvm::toString(JIT.takeError()) << "\n";
llvm::errs() << "Erro ao inicializar LLJIT: " << llvm::toString(JIT.takeError()) << "\n";
return 1;
}

std::cout << "(GalaxyJIT REPL v2.0.0) Enter code below ('exit' to quit):\n";
enter_scope();

llvm::IRBuilder<llvm::NoFolder> Builder(*Context);
llvm::Module TheModule("REPLModule", *Context);
std::cout << "(GalaxyJIT REPL v1.0.0) Enter code below ('exit' to quit):\n";

std::string Line;

std::stack<std::string> declarationStack; // Pilha para armazenar declarações e atribuições
std::vector<std::string> codeLines; // Vetor para armazenar o código gerado

while (true) {
// Abrir arquivo para escrita das declarações
FILE *outFile = fopen("repl.glx", "a");
if (!outFile) {
std::cerr << "Error opening file to write.\n";
return 1;
}

std::cout << ">>> ";
std::getline(std::cin, Line);
if (Line.empty()) continue;
if (Line == "exit") break;

// 1. Escrever as declarações no arquivo
writeDeclarationsToFile(declarationStack, outFile);

// 2. Adicionar a linha atual ao vetor de código
codeLines.push_back(Line);
// Criar um novo contexto, módulo e builder para cada iteração
llvm::LLVMContext TheContext;
auto TheModule = std::make_unique<llvm::Module>("GalaxyJIT", TheContext);
llvm::IRBuilder<llvm::NoFolder> Builder(TheContext);

// 3. Adicionar a linha ao arquivo imediatamente
// Criar um ResourceTracker para gerenciar o módulo desta iteração
auto ResourceTracker = (*JIT)->getMainJITDylib().createResourceTracker();

// Concatenar todas as linhas do código em uma única string, separadas por '\n'
std::string concatenatedCode = "";
for (const auto& code : codeLines) {
concatenatedCode += code + "\n";
FILE *tempFile = fopen("repl.glx", "w");
if (!tempFile) {
std::cerr << "Erro ao abrir arquivo temporário.\n";
return 1;
}

fputs(concatenatedCode.c_str(), outFile);
fprintf(tempFile, "%s\n", Line.c_str());
fclose(tempFile);

// 4. Gerar AST a partir da entrada do código concatenado
tempFile = fopen("repl.glx", "r");
int count = 0;
Token *tokenArray = tokenize(outFile, "repl.glx", &count); // Passando FILE*, nome do arquivo e count
Token *tokens = tokenize(tempFile, "repl.glx", &count);
fclose(tempFile);

// Gerar AST com base nos tokens
Parser parser = parser_new();
AstNode *ast = produce_ast(&parser, tokenArray, count, true); // Função fornecida por você
AstNode *ast = produce_ast(&parser, tokens, count, true);
if (!ast) {
std::cerr << "Error: Invalid input or failed to parse AST.\n";
std::cerr << "Erro: Entrada inválida ou falha ao analisar AST.\n";
freeTokens(tokens, count);
continue;
}

// Criar a função entry.main
llvm::FunctionType *mainFuncType = llvm::FunctionType::get(Builder.getVoidTy(), false);
llvm::Function *mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, "entry.main", *TheModule);

llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(TheContext, "entry", mainFunc);
Builder.SetInsertPoint(entryBlock);

generate_ir(ast, TheContext, *TheModule, Builder);

Builder.CreateRetVoid();

// Verificar o módulo
std::string errorMsg;
llvm::raw_string_ostream errorStream(errorMsg);
if (llvm::verifyModule(*TheModule, &errorStream)) {
llvm::errs() << "Erro ao verificar módulo:\n" << errorStream.str() << "\n";
free_ast_node(ast);
freeTokens(tokens, count);
continue;
}

if (ast->kind == NODE_PROGRAM) {
ProgramNode* programNode = (ProgramNode*)ast->data;

if (programNode->statement_count == 0) {
continue;
}

// 5. Processar os statements da AST
for (size_t i = 0; i < programNode->statement_count; ++i) {
processAstNode(programNode->statements[i], Line, outFile, declarationStack, codeLines);
}

// 6. criar a função main
createMainFunc(TheModule, *Context, Builder);

// Adicionar módulos externos ao JIT (libs)
std::vector<std::string> modules { "libs/std.ll" };
for (const auto &file : modules) {
llvm::SMDiagnostic Err;

// Carregar o módulo
auto Module = llvm::parseIRFile(file, Err, *Context);
if (!Module) {
llvm::errs() << "Erro ao carregar o arquivo " << file << ": " << Err.getMessage() << "\n";
continue; // Pula para o próximo módulo
}

// Adicionar o módulo ao JIT
if (auto Err = (*JIT)->addIRModule(llvm::orc::ThreadSafeModule(std::move(Module), std::make_unique<llvm::LLVMContext>()))) {
llvm::errs() << "Erro ao adicionar o módulo " << file << " ao JIT: " << llvm::toString(std::move(Err)) << "\n";
continue; // Pula para o próximo módulo
}
}

llvm::orc::ThreadSafeModule safeModule(std::make_unique<llvm::Module>("REPLModule", *Context), std::make_unique<llvm::LLVMContext>());
(*JIT)->addIRModule(std::move(safeModule));

// 7. Executar a função 'entry.main' após adicionar o módulo
auto entryMainSymbol = (*JIT)->lookup("entry.main");
if (entryMainSymbol) {
auto *entryMainFunc = (void (*)())entryMainSymbol->getValue();
entryMainFunc(); // Executa a função 'entry.main'
} else {
std::cerr << "Error: entry.main not found.\n";
}
// Adicionar o módulo ao JIT (sem usar o ResourceTracker diretamente)
if (auto Err = (*JIT)->addIRModule(llvm::orc::ThreadSafeModule(std::move(TheModule), std::make_unique<llvm::LLVMContext>()))) {
llvm::errs() << "Erro ao adicionar o módulo IR ao JIT: " << llvm::toString(std::move(Err)) << "\n";
free_ast_node(ast);
freeTokens(tokens, count);
return 1;
}

// Limpeza da AST e tokens após o processamento
free_ast_node(ast);
freeTokens(tokenArray, count);
// Executar a função entry.main
auto Func = (*JIT)->lookup("entry.main");
if (!Func) {
llvm::errs() << "Erro: entry.main não encontrado.\n";
free_ast_node(ast);
freeTokens(tokens, count);
continue;
}

std::cout << "Processed input and updated stacks.\n";
auto EntryMain = (void (*)())(Func->getValue());
EntryMain();

fclose(outFile); // Fechar o arquivo após a execução do REPL
free_ast_node(ast);
freeTokens(tokens, count);
}

return 0;
Expand Down

0 comments on commit 7e186d4

Please sign in to comment.