Add while loops, GC mark-and-sweep, and malloc block reuse

- Lexer: add INDENT/DEDENT tokens, <, >, : operators, while keyword,
  closing DEDENT emission, include guards
- Parser: add NODE_WHILE with while_loop union, parse while/cond/body
  blocks, include guards
- Eval: add while loop evaluation, GC integration with roots from env,
  debug and gc flags, <, > comparison operators
- GC: implement mark-and-sweep collector with 3 stages (mark roots,
  sweep unmarked, join free blocks)
- Allocator: block reuse via first-fit search with splitting, exponential
  heap growth, NULL check on malloc, include guards, marked field in metadata
- Object: add include guards, fix include to use allocator.h
This commit is contained in:
Jose Luis Montañes Ojados
2026-02-16 04:55:52 +01:00
parent 14b6a2ddd2
commit 84b3abbfda
8 changed files with 542 additions and 286 deletions

View File

@@ -1,3 +1,6 @@
#ifndef JLANG_ALLOCATOR_H
#define JLANG_ALLOCATOR_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -24,7 +27,7 @@
typedef struct {
size_t size; // 8 bytes
int in_use; // 4 bytes
// --- aquí el compilador mete 4 bytes invisibles ---
int marked; // 4 bytes
} JLANG_metadata;
/*
@@ -94,44 +97,110 @@ void *JLANG_last_free(void *ptr) {
size_t JLANG_malloc(void *ptr, size_t size) {
JLANG_memory_allocator *allocPtr = (JLANG_memory_allocator *)ptr;
// 1. Hay bloques disponibles??
size_t used = JLANG_used(ptr);
size_t available = allocPtr->size - used;
// Recorrer memoria hasta encontrar un hueco libre
JLANG_metadata *currentHead = (JLANG_metadata *)allocPtr->memory;
size_t used = 0;
while (1) {
used += currentHead->size;
if (currentHead->in_use == 0 && currentHead->size >= size) {
break;
}
if (currentHead->size == 0) {
size_t offset = (char *)currentHead - allocPtr->memory;
if (offset + sizeof(JLANG_metadata) + size > allocPtr->size) {
// No cabe, grow
size_t newSize = allocPtr->size * 2;
// asegurar que quepa la peticion
while (newSize < offset + sizeof(JLANG_metadata) + size) {
newSize *= 2;
}
char *newBuffer = (char *)malloc(newSize); // pedir newSize bytes
if (newBuffer == NULL) {
printf("ERROR: out of memory\n");
exit(1);
}
memset(newBuffer, 0, newSize);
memcpy(newBuffer, allocPtr->memory,
allocPtr->size); // Copiamos al nuevo buffer
allocPtr->size = newSize;
free(allocPtr->memory); // Liberamos el buffer antiguo
allocPtr->memory = newBuffer; // Seteamos el nuevo buffer
JLANG_metadata *freeHeader =
(JLANG_metadata *)(allocPtr->memory + offset);
freeHeader->size = size;
freeHeader->in_use = 1;
void *payloadPtr = (char *)freeHeader + sizeof(JLANG_metadata);
return (char *)payloadPtr - allocPtr->memory;
}
// Fin del heap, asignar aqui
currentHead->size = size;
currentHead->in_use = 1;
return (char *)currentHead - allocPtr->memory + sizeof(JLANG_metadata);
}
// Go next
currentHead =
(JLANG_metadata *)((char *)currentHead + sizeof(JLANG_metadata) +
currentHead->size);
}
// currentHead tiene sitio para nosotros, particionamos y usamos
currentHead->in_use = 1;
size_t oldSize = currentHead->size;
currentHead->size = size;
if (oldSize > size + sizeof(JLANG_metadata)) {
// creamos un bloque vacio con los bytes que sobran
JLANG_metadata *emptyBlock =
(JLANG_metadata *)((char *)currentHead + sizeof(JLANG_metadata) + size);
emptyBlock->size = oldSize - size - sizeof(JLANG_metadata);
}
return (char *)currentHead - allocPtr->memory + sizeof(JLANG_metadata);
/*
// 1. Hay bloques disponibles??
size_t used = JLANG_used(ptr);
size_t available = allocPtr->size - used;
// chequear si hay suficiente espacio libre
if (available > size + sizeof(JLANG_metadata)) {
JLANG_metadata *freeHeader = (JLANG_metadata *)JLANG_last_free(ptr);
freeHeader->size = size;
freeHeader->in_use = 1;
void *payloadPtr = (char *)freeHeader + sizeof(JLANG_metadata);
return (char *)payloadPtr - allocPtr->memory;
}
// Se necesita más espacio..
int newSize = allocPtr->size + 1024;
char *newBuffer = (char *)malloc(newSize); // Otro 1KB
memset(newBuffer, 0, newSize);
memcpy(newBuffer, allocPtr->memory,
allocPtr->size); // Copiamos al nuevo buffer
allocPtr->size = newSize;
free(allocPtr->memory); // Liberamos el buffer antiguo
allocPtr->memory = newBuffer; // Seteamos el nuevo buffer
// chequear si hay suficiente espacio libre
if (available > size + sizeof(JLANG_metadata)) {
JLANG_metadata *freeHeader = (JLANG_metadata *)JLANG_last_free(ptr);
freeHeader->size = size;
freeHeader->in_use = 1;
void *payloadPtr = (char *)freeHeader + sizeof(JLANG_metadata);
return (char *)payloadPtr - allocPtr->memory;
}
/*
Se necesita más espacio..
De momento vamos a pedir otro array más grande y liberar el antiguo
Mantenemos toda la memoria contigua
*/
int newSize = allocPtr->size + 1024;
char *newBuffer = (char *)malloc(newSize); // Otro 1KB
memset(newBuffer, 0, newSize);
memcpy(newBuffer, allocPtr->memory,
allocPtr->size); // Copiamos al nuevo buffer
allocPtr->size = newSize;
free(allocPtr->memory); // Liberamos el buffer antiguo
allocPtr->memory = newBuffer; // Seteamos el nuevo buffer
JLANG_metadata *freeHeader = (JLANG_metadata *)JLANG_last_free(ptr);
freeHeader->size = size;
freeHeader->in_use = 1;
void *payloadPtr = (char *)freeHeader + sizeof(JLANG_metadata);
return (char *)payloadPtr - allocPtr->memory;
}
void JLANG_free(void *ptr, size_t blockOffset) {
@@ -254,3 +323,5 @@ void JLANG_visualize(void *ptr) {
printf("]\n");
}
}
#endif