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:
4
projects/while.j
Normal file
4
projects/while.j
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
x = 0
|
||||||
|
while x < 100000000:
|
||||||
|
x = x + 1
|
||||||
|
print x
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#ifndef JLANG_LEXER_H
|
||||||
|
#define JLANG_LEXER_H
|
||||||
/*
|
/*
|
||||||
Convierte texto en una lista de tokens
|
Convierte texto en una lista de tokens
|
||||||
*/
|
*/
|
||||||
@@ -67,6 +69,8 @@ Token *tokenize(const char *source, int *token_count) {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
|
int indent_level = 0;
|
||||||
|
|
||||||
while (source[pos] != '\0' && pos < strlen(source)) {
|
while (source[pos] != '\0' && pos < strlen(source)) {
|
||||||
char c = source[pos];
|
char c = source[pos];
|
||||||
|
|
||||||
@@ -75,6 +79,20 @@ Token *tokenize(const char *source, int *token_count) {
|
|||||||
} else if (c == '\n') {
|
} else if (c == '\n') {
|
||||||
tokens[count++] = make_token(TOK_NEWLINE, "\n");
|
tokens[count++] = make_token(TOK_NEWLINE, "\n");
|
||||||
pos++;
|
pos++;
|
||||||
|
|
||||||
|
// Contar espacios al inicio de la linea
|
||||||
|
int spaces = 0;
|
||||||
|
while (source[pos] == ' ') {
|
||||||
|
spaces++;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
int new_level = spaces / 4; // 4 espacios = 1 nivel
|
||||||
|
if (new_level > indent_level) {
|
||||||
|
tokens[count++] = make_token(TOK_INDENT, "INDENT");
|
||||||
|
} else if (new_level < indent_level) {
|
||||||
|
tokens[count++] = make_token(TOK_DEDENT, "DEDENT");
|
||||||
|
}
|
||||||
|
indent_level = new_level;
|
||||||
} else if (c == '+') {
|
} else if (c == '+') {
|
||||||
tokens[count++] = make_token(TOK_PLUS, "+");
|
tokens[count++] = make_token(TOK_PLUS, "+");
|
||||||
pos++;
|
pos++;
|
||||||
@@ -90,6 +108,15 @@ Token *tokenize(const char *source, int *token_count) {
|
|||||||
} else if (c == '=') {
|
} else if (c == '=') {
|
||||||
tokens[count++] = make_token(TOK_ASSIGN, "=");
|
tokens[count++] = make_token(TOK_ASSIGN, "=");
|
||||||
pos++;
|
pos++;
|
||||||
|
} else if (c == '<') {
|
||||||
|
tokens[count++] = make_token(TOK_LT, "<");
|
||||||
|
pos++;
|
||||||
|
} else if (c == '>') {
|
||||||
|
tokens[count++] = make_token(TOK_GT, ">");
|
||||||
|
pos++;
|
||||||
|
} else if (c == ':') {
|
||||||
|
tokens[count++] = make_token(TOK_COLON, ":");
|
||||||
|
pos++;
|
||||||
} else if (c >= '0' && c <= '9') {
|
} else if (c >= '0' && c <= '9') {
|
||||||
// Leer todos los digitos consecutivos
|
// Leer todos los digitos consecutivos
|
||||||
int start = pos;
|
int start = pos;
|
||||||
@@ -106,6 +133,8 @@ Token *tokenize(const char *source, int *token_count) {
|
|||||||
// Comprobar si es una keyword reservada
|
// Comprobar si es una keyword reservada
|
||||||
if (strcmp(word, "print") == 0) {
|
if (strcmp(word, "print") == 0) {
|
||||||
tokens[count++] = make_token(TOK_PRINT, word);
|
tokens[count++] = make_token(TOK_PRINT, word);
|
||||||
|
} else if (strcmp(word, "while") == 0) {
|
||||||
|
tokens[count++] = make_token(TOK_WHILE, word);
|
||||||
} else {
|
} else {
|
||||||
tokens[count++] = make_token(TOK_ID, word);
|
tokens[count++] = make_token(TOK_ID, word);
|
||||||
}
|
}
|
||||||
@@ -115,6 +144,15 @@ Token *tokenize(const char *source, int *token_count) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cerrar indents pendientes
|
||||||
|
|
||||||
|
while (indent_level > 0) {
|
||||||
|
tokens[count++] = make_token(TOK_DEDENT, "DEDENT");
|
||||||
|
indent_level--;
|
||||||
|
}
|
||||||
|
|
||||||
*token_count = count;
|
*token_count = count;
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,208 +1,226 @@
|
|||||||
|
#ifndef JLANG_PARSER_H
|
||||||
|
#define JLANG_PARSER_H
|
||||||
/*
|
/*
|
||||||
Convierte tokens en un arbol
|
Convierte tokens en un arbol
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
NODE_INT_LIT, // literal entero
|
||||||
NODE_INT_LIT, // literal entero
|
NODE_STRING_LIT, // literal string
|
||||||
NODE_STRING_LIT, // literal string
|
NODE_VAR, // referencia a variable
|
||||||
NODE_VAR, // referencia a variable
|
NODE_ASSIGN, // asignacion: x = expr
|
||||||
NODE_ASSIGN, // asignacion: x = expr
|
NODE_BINOP, // operacion binaria: a + b
|
||||||
NODE_BINOP, // operacion binaria: a + b
|
NODE_PRINT, // print(expr)
|
||||||
NODE_PRINT, // print(expr)
|
NODE_IF, // if cond: bloque
|
||||||
NODE_IF, // if cond: bloque
|
NODE_WHILE, // while cond: bloque
|
||||||
NODE_WHILE, // while cond: bloque
|
NODE_BLOCK, // secuencia de statements
|
||||||
NODE_BLOCK, // secuencia de statements
|
|
||||||
} NodeType;
|
} NodeType;
|
||||||
|
|
||||||
typedef struct ASTNode
|
typedef struct ASTNode {
|
||||||
{
|
NodeType type;
|
||||||
NodeType type;
|
union {
|
||||||
union
|
int int_val; // NODE_INT_LIT
|
||||||
{
|
char *string_val; // NODE_STRING_LIT
|
||||||
int int_val; // NODE_INT_LIT
|
struct {
|
||||||
char *string_val; // NODE_STRING_LIT
|
char *name;
|
||||||
struct
|
struct ASTNode *value;
|
||||||
{
|
} assign; // NODE_ASSIGN
|
||||||
char *name;
|
struct {
|
||||||
struct ASTNode *value;
|
char op;
|
||||||
} assign; // NODE_ASSIGN
|
struct ASTNode *left;
|
||||||
struct
|
struct ASTNode *right;
|
||||||
{
|
} binop; // NODE_BINOP
|
||||||
char op;
|
struct {
|
||||||
struct ASTNode *left;
|
struct ASTNode *expr;
|
||||||
struct ASTNode *right;
|
} print; // NODE_PRINT
|
||||||
} binop; // NODE_BINOP
|
struct {
|
||||||
struct
|
struct ASTNode **stmts;
|
||||||
{
|
int count;
|
||||||
struct ASTNode *expr;
|
} block; // NODE_BLOCK
|
||||||
} print; // NODE_PRINT
|
struct {
|
||||||
struct
|
struct ASTNode *cond;
|
||||||
{
|
struct ASTNode *body;
|
||||||
struct ASTNode **stmts;
|
} while_loop; // NODE_WHILE
|
||||||
int count;
|
} data;
|
||||||
} block; // NODE_BLOCK
|
|
||||||
} data;
|
|
||||||
} ASTNode;
|
} ASTNode;
|
||||||
|
|
||||||
ASTNode *make_node(NodeType type)
|
ASTNode *make_node(NodeType type) {
|
||||||
{
|
ASTNode *node = (ASTNode *)malloc(sizeof(ASTNode));
|
||||||
ASTNode *node = (ASTNode *)malloc(sizeof(ASTNode));
|
node->type = type;
|
||||||
node->type = type;
|
return node;
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
ASTNode *parse_term(Token *tokens)
|
ASTNode *parse_term(Token *tokens) {
|
||||||
{
|
if (tokens[pos].type == TOK_INT) {
|
||||||
if (tokens[pos].type == TOK_INT)
|
ASTNode *node = make_node(NODE_INT_LIT);
|
||||||
{
|
node->data.int_val = atoi(tokens[pos].value);
|
||||||
ASTNode *node = make_node(NODE_INT_LIT);
|
pos++;
|
||||||
node->data.int_val = atoi(tokens[pos].value);
|
return node;
|
||||||
|
} else if (tokens[pos].type == TOK_ID) {
|
||||||
|
ASTNode *node = make_node(NODE_VAR);
|
||||||
|
node->data.string_val = tokens[pos].value;
|
||||||
|
pos++;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
printf("ERROR: esperaba INT o ID, encontré tipo %d\n", tokens[pos].type);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode *parse_expr(Token *tokens) {
|
||||||
|
ASTNode *left = parse_term(tokens);
|
||||||
|
|
||||||
|
while (tokens[pos].type == TOK_PLUS || tokens[pos].type == TOK_MINUS ||
|
||||||
|
tokens[pos].type == TOK_STAR || tokens[pos].type == TOK_SLASH ||
|
||||||
|
tokens[pos].type == TOK_LT || tokens[pos].type == TOK_GT) {
|
||||||
|
char op = tokens[pos].value[0]; // +,-,*,/
|
||||||
|
pos++;
|
||||||
|
ASTNode *right = parse_term(tokens);
|
||||||
|
|
||||||
|
ASTNode *binop = make_node(NODE_BINOP);
|
||||||
|
binop->data.binop.op = op;
|
||||||
|
binop->data.binop.left = left;
|
||||||
|
binop->data.binop.right = right;
|
||||||
|
left = binop; // encadenar: (a + b) + c
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTNode *parse_statement(Token *tokens) {
|
||||||
|
if (tokens[pos].type == TOK_ID) {
|
||||||
|
char *name = tokens[pos].value;
|
||||||
|
pos++; // consumir ID
|
||||||
|
pos++; // consumir "="
|
||||||
|
ASTNode *value = parse_expr(tokens);
|
||||||
|
|
||||||
|
ASTNode *node = make_node(NODE_ASSIGN);
|
||||||
|
node->data.assign.name = name;
|
||||||
|
node->data.assign.value = value;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (tokens[pos].type == TOK_PRINT) {
|
||||||
|
pos++; // consumir "print"
|
||||||
|
ASTNode *expr = parse_expr(tokens);
|
||||||
|
|
||||||
|
ASTNode *node = make_node(NODE_PRINT);
|
||||||
|
node->data.print.expr = expr;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[pos].type == TOK_WHILE) {
|
||||||
|
pos++; // consumir while
|
||||||
|
ASTNode *cond = parse_expr(tokens);
|
||||||
|
pos++; // consumir ":"
|
||||||
|
pos++; // consumir NEWLINE
|
||||||
|
pos++; // consumir INDENT
|
||||||
|
|
||||||
|
// Parsear bloque de statements hasta DEDENT
|
||||||
|
ASTNode *body = make_node(NODE_BLOCK);
|
||||||
|
body->data.block.stmts = (ASTNode **)malloc(sizeof(ASTNode *) * 256);
|
||||||
|
body->data.block.count = 0;
|
||||||
|
while (tokens[pos].type != TOK_DEDENT) {
|
||||||
|
body->data.block.stmts[body->data.block.count++] =
|
||||||
|
parse_statement(tokens);
|
||||||
|
if (tokens[pos].type == TOK_NEWLINE) {
|
||||||
pos++;
|
pos++;
|
||||||
return node;
|
}
|
||||||
}
|
}
|
||||||
else if (tokens[pos].type == TOK_ID)
|
pos++; // Consumir DEDENT
|
||||||
{
|
|
||||||
ASTNode *node = make_node(NODE_VAR);
|
ASTNode *node = make_node(NODE_WHILE);
|
||||||
node->data.string_val = tokens[pos].value;
|
node->data.while_loop.cond = cond;
|
||||||
pos++;
|
node->data.while_loop.body = body;
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
printf("ERROR: esperaba INT o ID, encontré tipo %d\n", tokens[pos].type);
|
printf("ERROR: statement inesperado\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode *parse_expr(Token *tokens)
|
ASTNode *parse(Token *tokens, int token_count) {
|
||||||
{
|
ASTNode *block = make_node(NODE_BLOCK);
|
||||||
ASTNode *left = parse_term(tokens);
|
block->data.block.stmts = (ASTNode **)malloc(sizeof(ASTNode *) * 256);
|
||||||
|
block->data.block.count = 0;
|
||||||
|
|
||||||
while (tokens[pos].type == TOK_PLUS || tokens[pos].type == TOK_MINUS || tokens[pos].type == TOK_STAR || tokens[pos].type == TOK_SLASH)
|
while (pos < token_count) {
|
||||||
{
|
if (tokens[pos].type == TOK_NEWLINE) {
|
||||||
char op = tokens[pos].value[0]; // +,-,*,/
|
pos++; // Saltar newlines sueltos
|
||||||
pos++;
|
continue;
|
||||||
ASTNode *right = parse_term(tokens);
|
|
||||||
|
|
||||||
ASTNode *binop = make_node(NODE_BINOP);
|
|
||||||
binop->data.binop.op = op;
|
|
||||||
binop->data.binop.left = left;
|
|
||||||
binop->data.binop.right = right;
|
|
||||||
left = binop; // encadenar: (a + b) + c
|
|
||||||
}
|
}
|
||||||
return left;
|
block->data.block.stmts[block->data.block.count++] =
|
||||||
|
parse_statement(tokens);
|
||||||
|
|
||||||
|
// Consumir newline despues del statement
|
||||||
|
if (pos < token_count && tokens[pos].type == TOK_NEWLINE) {
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode *parse_statement(Token *tokens)
|
void ast_print(ASTNode *node, const char *prefix, int is_last) {
|
||||||
{
|
if (!node)
|
||||||
if (tokens[pos].type == TOK_ID)
|
return;
|
||||||
{
|
|
||||||
char *name = tokens[pos].value;
|
|
||||||
pos++; // consumir ID
|
|
||||||
pos++; // consumir "="
|
|
||||||
ASTNode *value = parse_expr(tokens);
|
|
||||||
|
|
||||||
ASTNode *node = make_node(NODE_ASSIGN);
|
printf("%s", prefix);
|
||||||
node->data.assign.name = name;
|
printf(is_last ? "`-- " : "|-- ");
|
||||||
node->data.assign.value = value;
|
|
||||||
return node;
|
// Construir nuevo prefijo para hijos
|
||||||
|
char new_prefix[256];
|
||||||
|
snprintf(new_prefix, sizeof(new_prefix), "%s%s", prefix,
|
||||||
|
is_last ? " " : "| ");
|
||||||
|
|
||||||
|
switch (node->type) {
|
||||||
|
case NODE_WHILE:
|
||||||
|
printf("NODE_WHILE\n");
|
||||||
|
ast_print(node->data.while_loop.cond, new_prefix, 0);
|
||||||
|
ast_print(node->data.while_loop.body, new_prefix, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE_INT_LIT:
|
||||||
|
printf("NODE_INT_LIT(%d)\n", node->data.int_val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE_STRING_LIT:
|
||||||
|
printf("NODE_STRING_LIT(\"%s\")\n", node->data.string_val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE_VAR:
|
||||||
|
printf("NODE_VAR(\"%s\")\n", node->data.string_val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE_ASSIGN:
|
||||||
|
printf("NODE_ASSIGN { name:\"%s\" }\n", node->data.assign.name);
|
||||||
|
ast_print(node->data.assign.value, new_prefix, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE_BINOP:
|
||||||
|
printf("NODE_BINOP('%c')\n", node->data.binop.op);
|
||||||
|
ast_print(node->data.binop.left, new_prefix, 0);
|
||||||
|
ast_print(node->data.binop.right, new_prefix, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE_PRINT:
|
||||||
|
printf("NODE_PRINT\n");
|
||||||
|
ast_print(node->data.print.expr, new_prefix, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE_BLOCK:
|
||||||
|
printf("NODE_BLOCK\n");
|
||||||
|
for (int i = 0; i < node->data.block.count; i++) {
|
||||||
|
ast_print(node->data.block.stmts[i], new_prefix,
|
||||||
|
i == node->data.block.count - 1);
|
||||||
}
|
}
|
||||||
if (tokens[pos].type == TOK_PRINT) {
|
break;
|
||||||
pos++; // consumir "print"
|
|
||||||
ASTNode *expr = parse_expr(tokens);
|
|
||||||
|
|
||||||
ASTNode*node = make_node(NODE_PRINT);
|
default:
|
||||||
node->data.print.expr = expr;
|
printf("UNKNOWN\n");
|
||||||
return node;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("ERROR: statement inesperado\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTNode *parse(Token *tokens, int token_count)
|
void ast_debug(ASTNode *node) { ast_print(node, "", 1); }
|
||||||
{
|
|
||||||
ASTNode *block = make_node(NODE_BLOCK);
|
|
||||||
block->data.block.stmts = (ASTNode **)malloc(sizeof(ASTNode *) * 256);
|
|
||||||
block->data.block.count = 0;
|
|
||||||
|
|
||||||
while (pos < token_count)
|
#endif
|
||||||
{
|
|
||||||
if (tokens[pos].type == TOK_NEWLINE)
|
|
||||||
{
|
|
||||||
pos++; // Saltar newlines sueltos
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
block->data.block.stmts[block->data.block.count++] = parse_statement(tokens);
|
|
||||||
|
|
||||||
// Consumir newline despues del statement
|
|
||||||
if (pos < token_count && tokens[pos].type == TOK_NEWLINE)
|
|
||||||
{
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast_print(ASTNode *node, const char *prefix, int is_last)
|
|
||||||
{
|
|
||||||
if (!node)
|
|
||||||
return;
|
|
||||||
|
|
||||||
printf("%s", prefix);
|
|
||||||
printf(is_last ? "`-- " : "|-- ");
|
|
||||||
|
|
||||||
// Construir nuevo prefijo para hijos
|
|
||||||
char new_prefix[256];
|
|
||||||
snprintf(new_prefix, sizeof(new_prefix), "%s%s", prefix, is_last ? " " : "| ");
|
|
||||||
|
|
||||||
switch (node->type)
|
|
||||||
{
|
|
||||||
case NODE_INT_LIT:
|
|
||||||
printf("NODE_INT_LIT(%d)\n", node->data.int_val);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NODE_STRING_LIT:
|
|
||||||
printf("NODE_STRING_LIT(\"%s\")\n", node->data.string_val);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NODE_VAR:
|
|
||||||
printf("NODE_VAR(\"%s\")\n", node->data.string_val);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NODE_ASSIGN:
|
|
||||||
printf("NODE_ASSIGN { name:\"%s\" }\n", node->data.assign.name);
|
|
||||||
ast_print(node->data.assign.value, new_prefix, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NODE_BINOP:
|
|
||||||
printf("NODE_BINOP('%c')\n", node->data.binop.op);
|
|
||||||
ast_print(node->data.binop.left, new_prefix, 0);
|
|
||||||
ast_print(node->data.binop.right, new_prefix, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NODE_PRINT:
|
|
||||||
printf("NODE_PRINT\n");
|
|
||||||
ast_print(node->data.print.expr, new_prefix, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NODE_BLOCK:
|
|
||||||
printf("NODE_BLOCK\n");
|
|
||||||
for (int i = 0; i < node->data.block.count; i++)
|
|
||||||
{
|
|
||||||
ast_print(node->data.block.stmts[i], new_prefix, i == node->data.block.count - 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf("UNKNOWN\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ast_debug(ASTNode *node)
|
|
||||||
{
|
|
||||||
ast_print(node, "", 1);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,7 +34,10 @@ int main(int argc, char **argv) {
|
|||||||
ast_debug(block);
|
ast_debug(block);
|
||||||
|
|
||||||
Environment env = {0};
|
Environment env = {0};
|
||||||
eval(block, &env, allocPtr);
|
eval(block, &env, allocPtr, 0, 1);
|
||||||
|
|
||||||
|
printf("heapSize=%zu\n", allocPtr->size);
|
||||||
|
// JLANG_visualize(allocPtr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
#ifndef JLANG_ALLOCATOR_H
|
||||||
|
#define JLANG_ALLOCATOR_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -24,7 +27,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
size_t size; // 8 bytes
|
size_t size; // 8 bytes
|
||||||
int in_use; // 4 bytes
|
int in_use; // 4 bytes
|
||||||
// --- aquí el compilador mete 4 bytes invisibles ---
|
int marked; // 4 bytes
|
||||||
} JLANG_metadata;
|
} JLANG_metadata;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -94,44 +97,110 @@ void *JLANG_last_free(void *ptr) {
|
|||||||
size_t JLANG_malloc(void *ptr, size_t size) {
|
size_t JLANG_malloc(void *ptr, size_t size) {
|
||||||
JLANG_memory_allocator *allocPtr = (JLANG_memory_allocator *)ptr;
|
JLANG_memory_allocator *allocPtr = (JLANG_memory_allocator *)ptr;
|
||||||
|
|
||||||
// 1. Hay bloques disponibles??
|
// Recorrer memoria hasta encontrar un hueco libre
|
||||||
size_t used = JLANG_used(ptr);
|
JLANG_metadata *currentHead = (JLANG_metadata *)allocPtr->memory;
|
||||||
size_t available = allocPtr->size - used;
|
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);
|
JLANG_metadata *freeHeader = (JLANG_metadata *)JLANG_last_free(ptr);
|
||||||
freeHeader->size = size;
|
freeHeader->size = size;
|
||||||
freeHeader->in_use = 1;
|
freeHeader->in_use = 1;
|
||||||
|
|
||||||
void *payloadPtr = (char *)freeHeader + sizeof(JLANG_metadata);
|
void *payloadPtr = (char *)freeHeader + sizeof(JLANG_metadata);
|
||||||
|
|
||||||
return (char *)payloadPtr - allocPtr->memory;
|
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) {
|
void JLANG_free(void *ptr, size_t blockOffset) {
|
||||||
@@ -254,3 +323,5 @@ void JLANG_visualize(void *ptr) {
|
|||||||
printf("]\n");
|
printf("]\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
95
src/memory/gc.h
Normal file
95
src/memory/gc.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#ifndef JLANG_GC_H
|
||||||
|
#define JLANG_GC_H
|
||||||
|
|
||||||
|
#include "../objects/object.h"
|
||||||
|
#include "allocator.h"
|
||||||
|
|
||||||
|
void gc_collect(JLANG_memory_allocator *allocPtr, size_t *roots,
|
||||||
|
int root_count) {
|
||||||
|
// Stage 1. Mark blocks
|
||||||
|
for (int i = 0; i < root_count; i++) {
|
||||||
|
// get blockHeader from object offset
|
||||||
|
JLANG_metadata *blockHeader =
|
||||||
|
(JLANG_metadata *)((char *)allocPtr->memory + roots[i] -
|
||||||
|
sizeof(JLANG_metadata));
|
||||||
|
|
||||||
|
// Mark block
|
||||||
|
blockHeader->marked = 1;
|
||||||
|
|
||||||
|
Object *objPtr = (Object *)((char *)allocPtr->memory + roots[i]);
|
||||||
|
if (objPtr->type == OBJ_LIST) {
|
||||||
|
JLANG_metadata *itemsHeader =
|
||||||
|
(JLANG_metadata *)((char *)allocPtr->memory +
|
||||||
|
objPtr->data.list_val.items -
|
||||||
|
sizeof(JLANG_metadata));
|
||||||
|
itemsHeader->marked = 1;
|
||||||
|
} else if (objPtr->type == OBJ_STRING) {
|
||||||
|
JLANG_metadata *itemsHeader =
|
||||||
|
(JLANG_metadata *)((char *)allocPtr->memory +
|
||||||
|
objPtr->data.string_val.chars -
|
||||||
|
sizeof(JLANG_metadata));
|
||||||
|
itemsHeader->marked = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage 2. Sweep memory
|
||||||
|
JLANG_metadata *currentHead = (JLANG_metadata *)allocPtr->memory;
|
||||||
|
while (currentHead->size != 0) {
|
||||||
|
size_t blockOffset = (char *)currentHead - (char *)allocPtr->memory;
|
||||||
|
|
||||||
|
if (currentHead->marked == 0 && currentHead->in_use) {
|
||||||
|
// Free block
|
||||||
|
JLANG_free(allocPtr, blockOffset + sizeof(JLANG_metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset mark
|
||||||
|
currentHead->marked = 0;
|
||||||
|
|
||||||
|
currentHead = (JLANG_metadata *)((char *)currentHead + currentHead->size +
|
||||||
|
sizeof(JLANG_metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stage 3. Join free blocks
|
||||||
|
currentHead = (JLANG_metadata *)allocPtr->memory;
|
||||||
|
while (currentHead->size != 0) {
|
||||||
|
|
||||||
|
if (currentHead->in_use == 0) {
|
||||||
|
// iterate until last next free block
|
||||||
|
JLANG_metadata *nextHeader =
|
||||||
|
(JLANG_metadata *)((char *)currentHead + sizeof(JLANG_metadata) +
|
||||||
|
currentHead->size);
|
||||||
|
JLANG_metadata *lastHeader = currentHead;
|
||||||
|
while (1) {
|
||||||
|
if (nextHeader->in_use == 1 || nextHeader->size == 0) {
|
||||||
|
// Volvemos atrás
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHeader = nextHeader;
|
||||||
|
nextHeader =
|
||||||
|
(JLANG_metadata *)((char *)nextHeader + sizeof(JLANG_metadata) +
|
||||||
|
nextHeader->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make from currentHead to nextHeader + nextHeader(size) one block
|
||||||
|
size_t startIndex = (char *)currentHead - (char *)allocPtr->memory;
|
||||||
|
size_t endIndex =
|
||||||
|
((char *)lastHeader + sizeof(JLANG_metadata) + lastHeader->size) -
|
||||||
|
(char *)allocPtr->memory;
|
||||||
|
|
||||||
|
// Set to 0
|
||||||
|
for (int i = 0; i < (endIndex - startIndex); i++) {
|
||||||
|
allocPtr->memory[i + startIndex] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create valid header
|
||||||
|
currentHead = (JLANG_metadata *) ((char *)allocPtr->memory + startIndex);
|
||||||
|
currentHead->size = (endIndex - startIndex) - sizeof(JLANG_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentHead = (JLANG_metadata *)((char *)currentHead + currentHead->size +
|
||||||
|
sizeof(JLANG_metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
|
#ifndef JLANG_OBJECT_H
|
||||||
|
#define JLANG_OBJECT_H
|
||||||
|
|
||||||
#include "../memory/allocator.h"
|
#include "../memory/allocator.h"
|
||||||
|
|
||||||
|
|
||||||
#define JLANG_RESOLVE(alloc, offset) \
|
#define JLANG_RESOLVE(alloc, offset) \
|
||||||
((void *)(((JLANG_memory_allocator *)(alloc))->memory + (offset)))
|
((void *)(((JLANG_memory_allocator *)(alloc))->memory + (offset)))
|
||||||
|
|
||||||
@@ -137,3 +141,5 @@ void obj_print(void *allocator, size_t offset, const char *preffix) {
|
|||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
167
src/vm/eval.h
167
src/vm/eval.h
@@ -1,99 +1,120 @@
|
|||||||
#include "../objects/object.h"
|
#ifndef JLANG_EVAL_H
|
||||||
#include "../frontend/parser.h"
|
#define JLANG_EVAL_H
|
||||||
|
|
||||||
typedef struct
|
#include "../frontend/parser.h"
|
||||||
{
|
#include "../memory/gc.h"
|
||||||
char *name;
|
|
||||||
size_t value; // offset al Object en el allocator
|
typedef struct {
|
||||||
|
char *name;
|
||||||
|
size_t value; // offset al Object en el allocator
|
||||||
} Variable;
|
} Variable;
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
Variable vars[256];
|
||||||
Variable vars[256];
|
int count;
|
||||||
int count;
|
|
||||||
} Environment;
|
} Environment;
|
||||||
|
|
||||||
size_t env_get(Environment *env, const char *name)
|
size_t env_get(Environment *env, const char *name) {
|
||||||
{
|
for (int i = 0; i < env->count; i++) {
|
||||||
for (int i = 0; i < env->count; i++)
|
if (strcmp(env->vars[i].name, name) == 0) {
|
||||||
{
|
return env->vars[i].value;
|
||||||
if (strcmp(env->vars[i].name, name) == 0)
|
|
||||||
{
|
|
||||||
return env->vars[i].value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
printf("ERROR: variable '%s' no definida\n", name);
|
printf("ERROR: variable '%s' no definida\n", name);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void env_set(Environment *env, const char *name, size_t value)
|
void env_set(Environment *env, const char *name, size_t value) {
|
||||||
{
|
for (int i = 0; i < env->count; i++) {
|
||||||
for (int i = 0; i < env->count; i++)
|
if (strcmp(env->vars[i].name, name) == 0) {
|
||||||
{
|
env->vars[i].value = value;
|
||||||
if (strcmp(env->vars[i].name, name) == 0)
|
return;
|
||||||
{
|
|
||||||
env->vars[i].value = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// No existe, añadir
|
// No existe, añadir
|
||||||
env->vars[env->count].name = (char *)name;
|
env->vars[env->count].name = (char *)name;
|
||||||
env->vars[env->count].value = value;
|
env->vars[env->count].value = value;
|
||||||
env->count++;
|
env->count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int step = 0;
|
int step = 0;
|
||||||
|
|
||||||
size_t eval(ASTNode *node, Environment *env, void *allocator) {
|
size_t eval(ASTNode *node, Environment *env, void *allocator, int debug, int gc) {
|
||||||
|
|
||||||
|
// Run GC
|
||||||
|
if (gc) {
|
||||||
|
size_t roots[256];
|
||||||
|
for (int i = 0; i < env->count; i++) {
|
||||||
|
roots[i] = env->vars[i].value;
|
||||||
|
}
|
||||||
|
gc_collect(allocator, roots, env->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug > 0) {
|
||||||
step++;
|
step++;
|
||||||
printf("===== VM Step: %d =====\n", step);
|
printf("===== VM Step: %d =====\n", step);
|
||||||
printf("executing node:\n");
|
printf("executing node:\n");
|
||||||
ast_debug(node);
|
ast_debug(node);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
JLANG_visualize(allocator);
|
JLANG_visualize(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case NODE_INT_LIT:
|
case NODE_INT_LIT:
|
||||||
return obj_new_int(allocator, node->data.int_val);
|
return obj_new_int(allocator, node->data.int_val);
|
||||||
case NODE_VAR:
|
case NODE_VAR:
|
||||||
return env_get(env, node->data.string_val);
|
return env_get(env, node->data.string_val);
|
||||||
case NODE_ASSIGN: {
|
case NODE_ASSIGN: {
|
||||||
size_t val = eval(node->data.assign.value, env, allocator);
|
size_t val = eval(node->data.assign.value, env, allocator, debug, gc);
|
||||||
env_set(env, node->data.assign.name, val);
|
env_set(env, node->data.assign.name, val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
case NODE_BINOP: {
|
case NODE_BINOP: {
|
||||||
size_t left = eval(node->data.binop.left, env, allocator);
|
size_t left = eval(node->data.binop.left, env, allocator, debug, gc);
|
||||||
size_t right = eval(node->data.binop.right, env, allocator);
|
size_t right = eval(node->data.binop.right, env, allocator, debug, gc);
|
||||||
|
|
||||||
// Resolver objects
|
// Resolver objects
|
||||||
Object* l = (Object *) JLANG_RESOLVE(allocator, left);
|
Object *l = (Object *)JLANG_RESOLVE(allocator, left);
|
||||||
Object* r = (Object *) JLANG_RESOLVE(allocator, right);
|
Object *r = (Object *)JLANG_RESOLVE(allocator, right);
|
||||||
|
|
||||||
// Operar (ints por ahora)
|
// Operar (ints por ahora)
|
||||||
if (node->data.binop.op == '+'){
|
if (node->data.binop.op == '+') {
|
||||||
return obj_new_int(allocator, l->data.int_val + r->data.int_val);
|
return obj_new_int(allocator, l->data.int_val + r->data.int_val);
|
||||||
} else if (node->data.binop.op == '-') {
|
} else if (node->data.binop.op == '-') {
|
||||||
return obj_new_int(allocator, l->data.int_val - r->data.int_val);
|
return obj_new_int(allocator, l->data.int_val - r->data.int_val);
|
||||||
} else if (node->data.binop.op == '*') {
|
} else if (node->data.binop.op == '*') {
|
||||||
return obj_new_int(allocator, l->data.int_val * r->data.int_val);
|
return obj_new_int(allocator, l->data.int_val * r->data.int_val);
|
||||||
} else if (node->data.binop.op == '/') {
|
} else if (node->data.binop.op == '/') {
|
||||||
return obj_new_int(allocator, l->data.int_val / r->data.int_val);
|
return obj_new_int(allocator, l->data.int_val / r->data.int_val);
|
||||||
}
|
} else if (node->data.binop.op == '<') {
|
||||||
}
|
return obj_new_int(allocator, l->data.int_val < r->data.int_val);
|
||||||
case NODE_PRINT: {
|
} else if (node->data.binop.op == '>') {
|
||||||
size_t val = eval(node->data.print.expr, env, allocator);
|
return obj_new_int(allocator, l->data.int_val > r->data.int_val);
|
||||||
obj_print(allocator, val, "");
|
|
||||||
printf("\n");
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
case NODE_BLOCK:
|
|
||||||
for (int i=0; i< node->data.block.count; i++)
|
|
||||||
eval(node->data.block.stmts[i], env, allocator);
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
case NODE_PRINT: {
|
||||||
|
size_t val = eval(node->data.print.expr, env, allocator, debug, gc);
|
||||||
|
obj_print(allocator, val, "");
|
||||||
|
printf("\n");
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
case NODE_BLOCK:
|
||||||
|
for (int i = 0; i < node->data.block.count; i++)
|
||||||
|
eval(node->data.block.stmts[i], env, allocator, debug, gc);
|
||||||
|
return 0;
|
||||||
|
case NODE_WHILE:
|
||||||
|
while (1) {
|
||||||
|
size_t cond = eval(node->data.while_loop.cond, env, allocator, debug, gc);
|
||||||
|
Object *obj = (Object *)JLANG_RESOLVE(allocator, cond);
|
||||||
|
if (obj->data.int_val == 0)
|
||||||
|
break;
|
||||||
|
eval(node->data.while_loop.body, env, allocator, debug, gc);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user