commit 74b426e12db7ffbe9ecfdc4fecbdd9a3332bf887 Author: Jose Luis Montañes Ojados Date: Sun Feb 15 20:48:01 2026 +0100 first commit diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..c4d59aa --- /dev/null +++ b/build.bat @@ -0,0 +1 @@ +gcc -o run.exe src\main.c \ No newline at end of file diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..ddff5b6 --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,448 @@ +# j-lang: Roadmap detallado + +Guia de implementacion fase por fase. Cada fase construye sobre la anterior. + +``` +src/ + memory/ -> Fase 1 y 5 + objects/ -> Fase 2 + frontend/ -> Fase 3 + vm/ -> Fase 4 + main.c -> Punto de entrada (une todo) +``` + +--- + +## Fase 1: Gestion de Memoria + +**Objetivo:** Tener un `malloc` y `free` propios que gestionen un heap simulado con metadatos compactos. + +**Archivos:** `src/memory/allocator.h` + +**Estado actual:** Ya existe una implementacion en `mem-heap/src/allocator.h` con: +- `CMA_metadata` (struct con `size` y `in_use`) +- `CMA_CreateAllocator()` - crea un heap de 1KB +- `CMA_malloc()` - asigna bloques (solo append, no reutiliza huecos) +- `CMA_used()` - calcula bytes usados +- `CMA_last_free()` - encuentra el primer bloque libre +- `CMA_visualize()` - dibuja el heap en consola con colores + +### Tareas + +#### 1.1 Copiar allocator al proyecto +- Copiar `mem-heap/src/allocator.h` a `src/memory/allocator.h` +- Verificar que compila desde `main.c` con `#include "memory/allocator.h"` + +#### 1.2 Implementar `CMA_free` +Liberar un bloque marcandolo como no-usado. + +```c +// Firma +void CMA_free(void *allocator, void *ptr); + +// Logica: +// 1. ptr apunta al payload, retroceder sizeof(CMA_metadata) para llegar al header +// 2. Poner header->in_use = 0 +// 3. Opcionalmente: poner a cero los bytes del payload (memset) +``` + +#### 1.3 Reutilizar bloques liberados (first-fit) +Actualmente `CMA_malloc` solo hace append al final. Hay que recorrer el heap buscando bloques libres que quepan. + +```c +// En CMA_malloc, antes de ir al final: +// 1. Recorrer bloques desde el inicio +// 2. Si encuentras uno con in_use == 0 && size >= requested_size -> reutilizarlo +// 3. Si no hay ninguno, seguir con el append actual +``` + +#### 1.4 Compactar la cabecera (opcional, mejora) +El readme menciona compactar Size + Marked + InUse en menos bytes. La idea: +- Usar bitfields o empaquetar en un `uint32_t`: + - Bits 0-29: size (hasta ~1GB) + - Bit 30: marked (para el GC, Fase 5) + - Bit 31: in_use + +```c +typedef struct { + uint32_t header; // size:30 | marked:1 | in_use:1 +} CMA_metadata_compact; + +#define META_SIZE(h) ((h).header >> 2) +#define META_MARKED(h) (((h).header >> 1) & 1) +#define META_INUSE(h) ((h).header & 1) +``` + +### Criterio de terminado +- `CMA_malloc` asigna bloques correctamente +- `CMA_free` libera bloques +- `CMA_malloc` reutiliza bloques liberados +- `CMA_visualize` muestra bloques libres vs ocupados correctamente +- Test: malloc -> free -> malloc reutiliza el mismo espacio + +--- + +## Fase 2: Modelo de Objetos + +**Objetivo:** Definir como se representan los valores del lenguaje (numeros, strings, listas) en memoria C. + +**Archivos:** `src/objects/object.h` + +**Dependencias:** Fase 1 (los objetos se crean con `CMA_malloc`) + +### Tareas + +#### 2.1 Definir los tipos + +```c +typedef enum { + OBJ_INT, + OBJ_FLOAT, + OBJ_STRING, + OBJ_LIST, + OBJ_NONE // equivalente a None/null +} ObjectType; +``` + +#### 2.2 Definir el struct Object + +Enfoque con tagged union: + +```c +typedef struct Object { + ObjectType type; + union { + int int_val; // OBJ_INT + double float_val; // OBJ_FLOAT + struct { // OBJ_STRING + char *chars; + int length; + } string_val; + struct { // OBJ_LIST + struct Object **items; + int count; + int capacity; + } list_val; + } data; +} Object; +``` + +#### 2.3 Funciones constructoras + +Cada tipo necesita una funcion que cree el objeto usando el allocator: + +```c +Object* obj_new_int(void *allocator, int value); +Object* obj_new_string(void *allocator, const char *str); +Object* obj_new_list(void *allocator); +``` + +#### 2.4 Funcion de impresion + +```c +void obj_print(Object *obj); +// OBJ_INT -> printf("%d", obj->data.int_val) +// OBJ_STRING -> printf("%s", obj->data.string_val.chars) +// OBJ_LIST -> printf("["); ...cada elemento...; printf("]") +``` + +### Criterio de terminado +- Se pueden crear objetos de cada tipo con el allocator +- `obj_print` los muestra correctamente +- Test: crear int(42), string("hola"), lista con ambos, imprimir todo + +--- + +## Fase 3: Front-End (Lexer y Parser) + +**Objetivo:** Convertir texto fuente (`.j`) en un AST que la VM pueda ejecutar. + +**Archivos:** `src/frontend/lexer.h`, `src/frontend/parser.h` + +**Dependencias:** Fase 2 (el parser crea nodos AST que referencian tipos de Object) + +### Tareas + +#### 3.1 Lexer (Tokenizador) + +Convierte texto en una lista de tokens. + +```c +typedef enum { + // Literales + TOK_INT, // 42 + TOK_STRING, // "hola" + + // Identificadores y keywords + TOK_ID, // x, foo, mi_var + TOK_PRINT, // print (keyword) + TOK_IF, // if + TOK_WHILE, // while + + // Operadores + TOK_ASSIGN, // = + TOK_PLUS, // + + TOK_MINUS, // - + TOK_STAR, // * + TOK_SLASH, // / + TOK_EQ, // == + TOK_NEQ, // != + TOK_LT, // < + TOK_GT, // > + + // Delimitadores + TOK_LPAREN, // ( + TOK_RPAREN, // ) + TOK_COLON, // : + TOK_NEWLINE, // \n (significativo, como en Python) + TOK_INDENT, // aumento de indentacion + TOK_DEDENT, // reduccion de indentacion + + TOK_EOF +} TokenType; + +typedef struct { + TokenType type; + char *value; // texto literal del token + int line; // linea del fuente (para errores) +} Token; + +// Firma principal +Token* tokenize(const char *source, int *token_count); +``` + +Empezar simple: solo `TOK_INT`, `TOK_ID`, `TOK_ASSIGN`, `TOK_NEWLINE`, `TOK_EOF`. Ir agregando tokens conforme se necesiten. + +#### 3.2 Parser (AST) + +Convierte tokens en un arbol. + +```c +typedef enum { + NODE_INT_LIT, // literal entero: 42 + NODE_STRING_LIT, // literal string: "hola" + NODE_VAR, // referencia a variable: x + NODE_ASSIGN, // asignacion: x = expr + NODE_BINOP, // operacion binaria: a + b + NODE_PRINT, // print(expr) + NODE_IF, // if cond: bloque + NODE_WHILE, // while cond: bloque + NODE_BLOCK, // secuencia de statements +} NodeType; + +typedef struct ASTNode { + NodeType type; + union { + int int_val; // NODE_INT_LIT + char *string_val; // NODE_STRING_LIT, NODE_VAR + struct { char *name; struct ASTNode *value; } assign; // NODE_ASSIGN + struct { char op; struct ASTNode *left; struct ASTNode *right; } binop; // NODE_BINOP + struct { struct ASTNode *expr; } print; // NODE_PRINT + struct { struct ASTNode **stmts; int count; } block; // NODE_BLOCK + } data; +} ASTNode; + +// Firma principal +ASTNode* parse(Token *tokens, int token_count); +``` + +#### 3.3 Gramatica minima inicial + +Empezar con un subconjunto muy pequeno: + +``` +programa = { statement NEWLINE } +statement = ID "=" expr (asignacion) + | "print" expr (impresion) +expr = term { ("+" | "-") term } +term = INT | ID +``` + +Esto permite ejecutar cosas como: +``` +x = 10 +y = 20 +z = x + y +print z +``` + +### Criterio de terminado +- El lexer tokeniza `x = 10\nprint x` correctamente +- El parser genera un AST valido a partir de esos tokens +- Test: tokenizar y parsear `simple.j`, imprimir el arbol resultante + +--- + +## Fase 4: Evaluador / VM + +**Objetivo:** Recorrer el AST y ejecutar las instrucciones. + +**Archivos:** `src/vm/eval.h` + +**Dependencias:** Fase 2 (crea Objects), Fase 3 (recibe un AST) + +### Tareas + +#### 4.1 Tabla de variables (Environment) + +```c +typedef struct { + char *name; + Object *value; +} Variable; + +typedef struct { + Variable vars[256]; // simple, tamanio fijo por ahora + int count; +} Environment; + +// Buscar o crear variable +Object* env_get(Environment *env, const char *name); +void env_set(Environment *env, const char *name, Object *value); +``` + +#### 4.2 Funcion eval recursiva + +```c +Object* eval(ASTNode *node, Environment *env, void *allocator); + +// Logica por tipo de nodo: +// NODE_INT_LIT -> obj_new_int(allocator, node->data.int_val) +// NODE_VAR -> env_get(env, node->data.string_val) +// NODE_ASSIGN -> evaluar valor, env_set(name, resultado) +// NODE_BINOP -> evaluar left y right, operar segun op +// NODE_PRINT -> evaluar expr, obj_print(resultado) +// NODE_BLOCK -> evaluar cada statement en orden +``` + +#### 4.3 Flujo completo en main.c + +```c +int main(int argc, char *argv[]) { + // 1. Leer archivo .j + char *source = read_file(argv[1]); + + // 2. Tokenizar + int token_count; + Token *tokens = tokenize(source, &token_count); + + // 3. Parsear + ASTNode *program = parse(tokens, token_count); + + // 4. Ejecutar + void *allocator = CMA_CreateAllocator(); + Environment env = {0}; + eval(program, &env, allocator); + + return 0; +} +``` + +### Criterio de terminado +- `simple.j` con `x = 1` se ejecuta sin errores +- Un script con `x = 10\ny = 20\nprint x + y` imprime `30` +- Las variables se almacenan y recuperan correctamente + +--- + +## Fase 5: Recolector de Basura (GC) + +**Objetivo:** Liberar automaticamente objetos que ya no son accesibles. + +**Archivos:** `src/memory/gc.h` + +**Dependencias:** Fase 1 (usa `CMA_free`), Fase 2 (recorre Objects), Fase 4 (accede al Environment) + +### Tareas + +#### 5.1 Agregar bit `marked` al metadata + +Si se compacto la cabecera en Fase 1.4, ya esta. Si no, agregar un campo: + +```c +typedef struct { + size_t size; + int in_use; + int marked; // <-- nuevo +} CMA_metadata; +``` + +#### 5.2 Mark (Marcar) + +Recorrer todos los objetos alcanzables desde las variables del Environment y marcarlos. + +```c +void gc_mark_object(Object *obj); +// 1. Obtener el header del objeto (retroceder sizeof(CMA_metadata)) +// 2. Poner marked = 1 +// 3. Si es OBJ_LIST, marcar recursivamente cada elemento +// 4. Si es OBJ_STRING, marcar el buffer de chars + +void gc_mark(Environment *env); +// Para cada variable en env: gc_mark_object(variable.value) +``` + +#### 5.3 Sweep (Barrer) + +Recorrer todo el heap linealmente. Si un bloque tiene `in_use == 1` y `marked == 0`, liberarlo. + +```c +void gc_sweep(void *allocator); +// 1. Empezar desde el inicio del heap +// 2. Para cada bloque: +// - Si in_use && !marked -> CMA_free +// - Si marked -> quitar marked (resetear para el proximo ciclo) +// 3. Avanzar al siguiente bloque con (header + sizeof(CMA_metadata) + size) +``` + +#### 5.4 Integrar con el evaluador + +```c +// En eval(), despues de N asignaciones o cuando quede poca memoria: +void gc_collect(void *allocator, Environment *env) { + gc_mark(env); + gc_sweep(allocator); +} +``` + +### Criterio de terminado +- Crear objetos, perder la referencia, ejecutar GC -> la memoria se libera +- `CMA_visualize` muestra bloques liberados tras el GC +- Test: crear 100 objetos en un loop, verificar que la memoria no crece indefinidamente + +--- + +## Orden de implementacion recomendado + +``` +Fase 1.1 (copiar allocator) + | +Fase 1.2 (CMA_free) + | +Fase 1.3 (reutilizar bloques) + | +Fase 2 (model de objetos) + | +Fase 3.1 (lexer basico) + | +Fase 3.2 (parser basico) + | +Fase 4 (eval + main.c) <- aqui ya puedes ejecutar simple.j + | +Fase 1.4 (compactar cabecera) <- opcional, mejora + | +Fase 5 (GC) +``` + +## Test de integracion final + +Cuando todo este listo, este script deberia funcionar: + +```python +x = 10 +y = 20 +z = x + y +print z +``` + +Salida esperada: `30` diff --git a/projects/simple.j b/projects/simple.j new file mode 100644 index 0000000..3d2b4b1 --- /dev/null +++ b/projects/simple.j @@ -0,0 +1 @@ +x = 1 \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..66587ce --- /dev/null +++ b/readme.md @@ -0,0 +1,52 @@ +# j-lang + +La idea de j-lang es crear un "proto-lenguaje" parecido a python pero implementado desde 0 para validar y aprender más sobre la gestión de memoria. + +Actualmente en `mem-heap\src\allocator.h` ya hay una implementeción de un Memory Allocator casi funcional. + +## 🗺️ Hoja de Ruta: Proyecto Proto-Lenguaje +Esta ruta va desde lo más bajo (la memoria) hasta lo más alto (ejecutar código). + +### Fase 1: El Cimiento (Gestión de Memoria) 🏗️ +Objetivo: Tener un malloc y free propios que gestionen metadatos compactos. + +Estado: ¡Ya estás aquí! + +Tareas clave: +- [ ] Terminar CMA_malloc con la cabecera compactada (Size + Marked + InUse). +- [ ] Implementar una función CMA_free que pueda liberar un bloque específico. + +### Fase 2: El Modelo de Objetos (Object Model) 📦 +Objetivo: Definir cómo se ve un número, una cadena o una lista dentro de tu memoria C. +Conexión: Cada objeto de tu lenguaje será un struct en C que comienza con tu CMA_metadata. + +Tareas clave: +- [ ] Crear un enum para los tipos (ENTERO, STRING, LISTA). +- [ ] Definir el struct Object genérico que envuelve tus datos. + +### Fase 3: El Front-End (Lexer y Parser) 📖 +Objetivo: Convertir el texto del código fuente en algo que C entienda. + +Tareas clave: +- [ ] Lexer (Tokenizador): Romper el texto x = 10 en fichas: [ID:x], [OP:=], [NUM:10]. +- [ ] Parser: Organizar esas fichas en un Árbol de Sintaxis Abstracta (AST). Por ejemplo, un nodo "Asignación" que tiene un hijo "x" y otro "10". + +### Fase 4: El Motor (Evaluador o VM) ⚙️ +Objetivo: Recorrer el árbol y "hacer" lo que dice. + +Tareas clave: +- [ ] Crear una función recursiva eval(nodo) que ejecute la lógica. + +Si es un nodo SUMA, suma los hijos. Si es un nodo IMPRIMIR, muestra en pantalla. + +### Fase 5: El Recolector de Basura (Garbage Collector) 🧹 +Objetivo: Automatizar la limpieza. + +Tareas clave: +- [ ] Implementar Mark (Marcar): Recorrer todos los objetos accesibles desde tus variables y poner el bit Marked a 1. +- [ ] Implementar Sweep (Barrer): Recorrer todo el heap linealmente (usando tu función next_block). Si un bloque tiene Marked == 0 y InUse == 1, llamar a CMA_free. + +## Estructura del proyecto + +- vm: maquina virtual de j-lang +- projects: carpeta con scripts en j-lang \ No newline at end of file diff --git a/run.exe b/run.exe new file mode 100644 index 0000000..ec0091b Binary files /dev/null and b/run.exe differ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..e9cdae1 --- /dev/null +++ b/src/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} \ No newline at end of file