Add class support with constructors, fields, and methods

This commit is contained in:
Jose Luis Montañes Ojados
2026-02-19 04:24:44 +01:00
parent f2e90efc16
commit d8b4f9b2ea
9 changed files with 660 additions and 117 deletions

11
projects/classes.j Normal file
View File

@@ -0,0 +1,11 @@
class Dog:
fn init(self, name):
self.name = name
fn bark(self):
println("guau!")
d = Dog("ahi te va")
x = d.bark()
println("Hola ", d.name)
debugHeap()

View File

@@ -1,4 +1,8 @@
fn greet(name): x = "Hello world!"
println("Hola, " + name)
greet("mundo!") fn suma(x, y):
fn pow(z):
return z * z
return x + pow(y)
println(suma(2, 2))

View File

@@ -5,6 +5,18 @@
#include "opcodes.h" #include "opcodes.h"
#include <string.h> #include <string.h>
typedef struct {
char *method_name;
int entry_point;
int param_count;
char **param_names;
} MethodEntry;
typedef struct {
char *name;
MethodEntry methods[16];
int method_count;
} ClassEntry;
typedef struct { typedef struct {
char *name; char *name;
@@ -22,6 +34,8 @@ typedef struct {
int name_count; int name_count;
FunctionEntry functions[64]; FunctionEntry functions[64];
int func_count; int func_count;
ClassEntry classes[16];
int class_count;
} Chunk; } Chunk;
int emit(Chunk *chunk, Instruction instr) { int emit(Chunk *chunk, Instruction instr) {
@@ -86,6 +100,26 @@ int compile_node(Chunk *chunk, ASTNode *node) {
compile_node(chunk, node->data.call.args[i]); compile_node(chunk, node->data.call.args[i]);
} }
// Verificar si es constructor de una clase
for (int i = 0; i < chunk->class_count; i++) {
if (strcmp(chunk->classes[i].name, node->data.call.name) == 0) {
// Buscar init
for (int m = 0; m < chunk->classes[i].method_count; m++) {
if (strcmp(chunk->classes[i].methods[m].method_name, "init") == 0) {
int expected =
chunk->classes[i].methods[m].param_count - 1; // -1 por self
if (node->data.call.arg_count != expected) {
printf("error: %s() espera %d args, pero recibio %d\n",
node->data.call.name, expected, node->data.call.arg_count);
exit(1);
}
break;
}
}
break;
}
}
// Registrar el nombre de la funcion // Registrar el nombre de la funcion
Instruction instr = make_instruction(OP_CALL); Instruction instr = make_instruction(OP_CALL);
instr.operand.call.arg_count = node->data.call.arg_count; instr.operand.call.arg_count = node->data.call.arg_count;
@@ -170,6 +204,13 @@ int compile_node(Chunk *chunk, ASTNode *node) {
break; break;
} }
case NODE_RETURN: {
if (node->data.ret.value) {
compile_node(chunk, node->data.ret.value);
}
emit(chunk, make_instruction(OP_RETURN));
return 0;
}
case NODE_FN_DEF: { case NODE_FN_DEF: {
// emitir jmp para ignorar la funcion por defecto // emitir jmp para ignorar la funcion por defecto
Instruction jump = make_instruction(OP_JUMP); Instruction jump = make_instruction(OP_JUMP);
@@ -186,9 +227,9 @@ int compile_node(Chunk *chunk, ASTNode *node) {
// emitir store_var para cada parametro (orden inverso al stack) // emitir store_var para cada parametro (orden inverso al stack)
for (int i = node->data.fn_def.param_count - 1; i >= 0; i--) { for (int i = node->data.fn_def.param_count - 1; i >= 0; i--) {
Instruction store = make_instruction(OP_STORE_VAR); Instruction store = make_instruction(OP_STORE_VAR);
store.operand.var_index = add_name(chunk, node->data.fn_def.params[i]); store.operand.var_index = add_name(chunk, node->data.fn_def.params[i]);
emit(chunk, store); emit(chunk, store);
} }
// compilar el cuerpo // compilar el cuerpo
@@ -201,6 +242,80 @@ int compile_node(Chunk *chunk, ASTNode *node) {
chunk->code[jump_idx].operand.jump_target = chunk->code_count; chunk->code[jump_idx].operand.jump_target = chunk->code_count;
break; break;
} }
case NODE_CLASS_DEF: {
// Registrar ClassEntry
ClassEntry *cls = &chunk->classes[chunk->class_count++];
cls->name = node->data.class_def.name;
cls->method_count = 0;
// Pre-registrar self en la tabla de nombres
add_name(chunk, "self");
int totalMethods = node->data.class_def.method_count;
for (int i = 0; i < totalMethods; i++) {
ASTNode *method = node->data.class_def.methods[i];
// jump over
Instruction jump = make_instruction(OP_JUMP);
jump.operand.jump_target = -1;
int jump_idx = emit(chunk, jump);
// Registrar method entry
int entry = chunk->code_count;
MethodEntry *me = &cls->methods[cls->method_count++];
me->method_name = method->data.fn_def.name;
me->entry_point = entry;
me->param_count = method->data.fn_def.param_count;
me->param_names = method->data.fn_def.params;
// store_var para cada parametro (orden inverso)
for (int p = method->data.fn_def.param_count - 1; p >= 0; p--) {
Instruction store = make_instruction(OP_STORE_VAR);
store.operand.var_index =
add_name(chunk, method->data.fn_def.params[p]);
emit(chunk, store);
}
// Compilar body
compile_node(chunk, method->data.fn_def.body);
// return implicito
emit(chunk, make_instruction(OP_RETURN));
// Backpatch
chunk->code[jump_idx].operand.jump_target = chunk->code_count;
}
break;
}
case NODE_DOT_ACCESS: {
compile_node(chunk, node->data.dot_access.object);
Instruction instr = make_instruction(OP_GET_FIELD);
instr.operand.var_index = add_name(chunk, node->data.dot_access.field);
emit(chunk, instr);
break;
}
case NODE_DOT_ASSIGN: {
compile_node(chunk, node->data.dot_assign.value); // push valor
compile_node(chunk, node->data.dot_assign.object); // push instancia
Instruction instr = make_instruction(OP_SET_FIELD);
instr.operand.var_index = add_name(chunk, node->data.dot_assign.field);
emit(chunk, instr);
break;
}
case NODE_METHOD_CALL: {
compile_node(chunk, node->data.method_call.object); // push instancia
for (int i = 0; i < node->data.method_call.arg_count; i++) {
compile_node(chunk, node->data.method_call.args[i]);
}
Instruction instr = make_instruction(OP_CALL_METHOD);
instr.operand.call.name_index =
add_name(chunk, node->data.method_call.method);
instr.operand.call.arg_count = node->data.method_call.arg_count;
emit(chunk, instr);
break;
}
default: default:
break; break;
} }
@@ -298,12 +413,57 @@ void print_chunk(Chunk *chunk) {
case OP_HALT: case OP_HALT:
printf("HALT"); printf("HALT");
break; break;
case OP_GET_FIELD:
printf("GET_FIELD [%d] %s", instr.operand.var_index,
chunk->names[instr.operand.var_index]);
break;
case OP_SET_FIELD:
printf("SET_FIELD [%d] %s", instr.operand.var_index,
chunk->names[instr.operand.var_index]);
break;
case OP_CALL_METHOD:
printf("CALL_METHOD %s(%d args)",
chunk->names[instr.operand.call.name_index],
instr.operand.call.arg_count);
break;
default: default:
printf("UNKNOWN op=%d", instr.op); printf("UNKNOWN op=%d", instr.op);
break; break;
} }
printf("\n"); printf("\n");
} }
printf("=== User Functions ===\n");
for (int i = 0; i < chunk->func_count; i++) {
FunctionEntry *fn = &chunk->functions[i];
printf("[%.4d] %s(", fn->entry_point, fn->name);
for (int p = 0; p < fn->param_count; p++) {
printf("%s", fn->param_names[p]);
if (p < fn->param_count - 1) {
printf(", ");
}
}
printf(")\n");
}
printf("=== Classes ===\n");
for (int i = 0; i < chunk->class_count; i++) {
ClassEntry *cls = &chunk->classes[i];
printf("class %s (%d methods)\n", cls->name, cls->method_count);
for (int m = 0; m < cls->method_count; m++) {
MethodEntry *me = &cls->methods[m];
printf(" [%.4d] %s(", me->entry_point, me->method_name);
for (int p = 0; p < me->param_count; p++) {
printf("%s", me->param_names[p]);
if (p < me->param_count - 1)
printf(", ");
}
printf(")\n");
}
}
printf("=== End ===\n"); printf("=== End ===\n");
} }

View File

@@ -2,36 +2,42 @@
#define JLANG_OPCODES_H #define JLANG_OPCODES_H
typedef enum { typedef enum {
OP_CONST_INT, // push entero inmediato OP_CONST_INT, // push entero inmediato
OP_CONST_STRING, // push string desde pool de constantes (alloc en heap) OP_CONST_STRING, // push string desde pool de constantes (alloc en heap)
OP_POP, // descarta top del stack OP_POP, // descarta top del stack
OP_ADD, OP_SUB, OP_MUL, OP_DIV, // aritmetica OP_ADD,
OP_NEG, // negacion unaria OP_SUB,
OP_CMP_LT, OP_CMP_GT, // comparacion -> push 0 o 1 OP_MUL,
OP_LOAD_VAR, // push variable por indice OP_DIV, // aritmetica
OP_STORE_VAR, // pop -> guardar en variable por indice OP_NEG, // negacion unaria
OP_JUMP, // salto incondicional OP_CMP_LT,
OP_JUMP_IF_ZERO, // pop -> si false, saltar OP_CMP_GT, // comparacion -> push 0 o 1
OP_CALL, // llamar built-in por indice de nombre OP_LOAD_VAR, // push variable por indice
OP_RETURN, // retornar de funcion (pop call frame) OP_STORE_VAR, // pop -> guardar en variable por indice
OP_NOP, OP_HALT, OP_JUMP, // salto incondicional
OP_JUMP_IF_ZERO, // pop -> si false, saltar
OP_CALL, // llamar built-in por indice de nombre
OP_RETURN, // retornar de funcion (pop call frame)
OP_NOP,
OP_HALT,
OP_GET_FIELD, // TOS=instance, operand=name_idx → push field value
OP_SET_FIELD, // TOS=instance, TOS-1=value, operand=name_idx → set field
OP_CALL_METHOD, // TOS-N-1=instance + N args, operand={name_idx, arg_count}
} OpCode; } OpCode;
typedef struct typedef struct {
{ OpCode op;
OpCode op; union {
union int int_val; // OP_CONST_INT
{ int str_index; // OP_CONST_STRING: indice a pool de constantes
int int_val; // OP_CONST_INT int var_index; // OP_LOAD_VAR, OP_STORE_VAR
int str_index; // OP_CONST_STRING: indice a pool de constantes int jump_target; // OP_JUMP, OP_JUMP_IF_ZERO
int var_index; // OP_LOAD_VAR, OP_STORE_VAR struct {
int jump_target; // OP_JUMP, OP_JUMP_IF_ZERO int name_index;
struct int arg_count;
{ } call; // OP_CALL
int name_index; } operand;
int arg_count;
} call; // OP_CALL
} operand;
} Instruction; } Instruction;
#endif #endif

View File

@@ -10,6 +10,8 @@ typedef struct {
int saved_sp; // base del stack int saved_sp; // base del stack
Value saved_vars[256]; // variables del caller (snapshot) Value saved_vars[256]; // variables del caller (snapshot)
int saved_var_set[256]; int saved_var_set[256];
int is_constructor;
Value constructor_instance;
} CallFrame; } CallFrame;
typedef struct { typedef struct {
@@ -115,6 +117,7 @@ void run_vm(VM *vm) {
CallFrame *frame = &vm->frames[vm->frame_count++]; CallFrame *frame = &vm->frames[vm->frame_count++];
frame->return_ip = vm->ip + 1; // volver a la siguiente instruccion frame->return_ip = vm->ip + 1; // volver a la siguiente instruccion
frame->saved_sp = vm->sp - fn->param_count; frame->saved_sp = vm->sp - fn->param_count;
frame->is_constructor = 0;
memcpy(frame->saved_vars, vm->vars, sizeof(vm->vars)); memcpy(frame->saved_vars, vm->vars, sizeof(vm->vars));
memcpy(frame->saved_var_set, vm->var_set, sizeof(vm->var_set)); memcpy(frame->saved_var_set, vm->var_set, sizeof(vm->var_set));
@@ -123,6 +126,63 @@ void run_vm(VM *vm) {
continue; // no hacer ip++ continue; // no hacer ip++
} }
// Buscar en classes[]
ClassEntry *cls = NULL;
int class_idx = -1;
for (int i = 0; i < vm->chunk->class_count; i++) {
if (strcmp(vm->chunk->classes[i].name, name) == 0) {
cls = &vm->chunk->classes[i];
class_idx = i;
break;
}
}
if (cls != NULL) {
// alloc instancia
size_t instOffset =
obj_new_instance(vm->allocator, class_idx, 8, sizeof(Value));
Value instVal = {0};
instVal.type = VAL_OBJ;
instVal.as.heap_offset = instOffset;
// buscar init
MethodEntry *init = NULL;
for (int m = 0; m < cls->method_count; m++) {
if (strcmp(cls->methods[m].method_name, "init") == 0) {
init = &cls->methods[m];
break;
}
}
if (init != NULL) {
// insertar instancia bajo de los args para self
int nArgs = instr.operand.call.arg_count;
for (int a = vm->sp - 1; a >= vm->sp - nArgs; a--) {
vm->stack[a + 1] = vm->stack[a];
}
vm->stack[vm->sp - nArgs] = instVal;
vm->sp++;
// Save frame
CallFrame *frame = &vm->frames[vm->frame_count++];
frame->return_ip = vm->ip + 1;
frame->saved_sp = vm->sp - nArgs - 1;
frame->is_constructor = 1;
frame->constructor_instance = instVal;
memcpy(frame->saved_vars, vm->vars, sizeof(vm->vars));
;
memcpy(frame->saved_var_set, vm->var_set, sizeof(vm->var_set));
vm->ip = init->entry_point;
continue;
} else {
// No hay init, descartar args y devolver instancia
vm->sp -= instr.operand.call.arg_count;
vm->stack[vm->sp++] = instVal;
}
break;
}
if (strcmp(name, "print") == 0 || strcmp(name, "println") == 0) { if (strcmp(name, "print") == 0 || strcmp(name, "println") == 0) {
int nParams = instr.operand.call.arg_count; int nParams = instr.operand.call.arg_count;
@@ -178,7 +238,7 @@ void run_vm(VM *vm) {
// Captrurar valor de retorno si hay alguno en el stack // Captrurar valor de retorno si hay alguno en el stack
Value return_val = {0}; Value return_val = {0};
int has_return = 0; int has_return = 0;
if (vm->sp > vm->frames[vm->frame_count-1].saved_sp) { if (vm->sp > vm->frames[vm->frame_count - 1].saved_sp) {
return_val = vm->stack[--vm->sp]; return_val = vm->stack[--vm->sp];
has_return = 1; has_return = 1;
} }
@@ -191,8 +251,13 @@ void run_vm(VM *vm) {
memcpy(vm->var_set, frame->saved_var_set, sizeof(vm->var_set)); memcpy(vm->var_set, frame->saved_var_set, sizeof(vm->var_set));
// Push return value // Push return value
if (has_return) { if (frame->is_constructor) {
vm->stack[vm->sp++] = frame->constructor_instance;
} else if (has_return) {
vm->stack[vm->sp++] = return_val; vm->stack[vm->sp++] = return_val;
} else {
Value nil = {0};
vm->stack[vm->sp++] = nil;
} }
continue; continue;
@@ -322,6 +387,19 @@ void run_vm(VM *vm) {
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
if (vm->var_set[i] && vm->vars[i].type == VAL_OBJ) { if (vm->var_set[i] && vm->vars[i].type == VAL_OBJ) {
roots[root_count++] = vm->vars[i].as.heap_offset; roots[root_count++] = vm->vars[i].as.heap_offset;
// si es instancia agregar fields
Object *obj =
JLANG_RESOLVE(vm->allocator, vm->vars[i].as.heap_offset);
if (obj->type == OBJ_INSTANCE) {
Value *values = (Value *)JLANG_RESOLVE(
vm->allocator, obj->data.instance_val.field_values);
for (int f = 0; f < obj->data.instance_val.field_count; f++) {
if (values[f].type == VAL_OBJ) {
roots[root_count++] = values[f].as.heap_offset;
}
}
}
} }
} }
@@ -336,6 +414,98 @@ void run_vm(VM *vm) {
break; break;
} }
case OP_GET_FIELD: {
Value instance = vm->stack[--vm->sp];
Object *obj = JLANG_RESOLVE(vm->allocator, instance.as.heap_offset);
int name_idx = instr.operand.var_index;
int *names = (int *)JLANG_RESOLVE(vm->allocator,
obj->data.instance_val.field_names);
Value *values = (Value *)JLANG_RESOLVE(
vm->allocator, obj->data.instance_val.field_values);
int found = 0;
for (int i = 0; i < obj->data.instance_val.field_count; i++) {
if (names[i] == name_idx) {
vm->stack[vm->sp++] = values[i];
found = 1;
break;
}
}
if (!found) {
printf("error: field '%s' not found\n", vm->chunk->names[name_idx]);
return;
}
break;
}
case OP_SET_FIELD: {
Value instance = vm->stack[--vm->sp];
Value value = vm->stack[--vm->sp];
int name_idx = instr.operand.var_index;
Object *obj = JLANG_RESOLVE(vm->allocator, instance.as.heap_offset);
int *names = (int *)JLANG_RESOLVE(vm->allocator,
obj->data.instance_val.field_names);
Value *values = (Value *)JLANG_RESOLVE(
vm->allocator, obj->data.instance_val.field_values);
int found = 0;
for (int i = 0; i < obj->data.instance_val.field_count; i++) {
if (names[i] == name_idx) {
values[i] = value;
found = 1;
break;
}
}
if (!found) {
// Agregar campo nuevo
int idx = obj->data.instance_val.field_count++;
names[idx] = name_idx;
values[idx] = value;
}
break;
}
case OP_CALL_METHOD: {
int method_name_idx = instr.operand.call.name_index;
int arg_count = instr.operand.call.arg_count;
char *method_name = vm->chunk->names[method_name_idx];
// la instancia está bajo de los args
Value instance = vm->stack[vm->sp - arg_count - 1];
Object *obj = JLANG_RESOLVE(vm->allocator, instance.as.heap_offset);
ClassEntry *cls = &vm->chunk->classes[obj->data.instance_val.class_index];
// Buscar metodo
MethodEntry *method = NULL;
for (int i = 0; i < cls->method_count; i++) {
if (strcmp(cls->methods[i].method_name, method_name) == 0) {
method = &cls->methods[i];
break;
}
}
if (method == NULL) {
printf("error: method '%s' not found in class '%s'\n", method_name,
cls->name);
return;
}
// Save frame
CallFrame *frame = &vm->frames[vm->frame_count++];
frame->return_ip = vm->ip + 1;
frame->saved_sp = vm->sp - arg_count - 1;
frame->is_constructor = 0;
memcpy(frame->saved_vars, vm->vars, sizeof(vm->vars));
memcpy(frame->saved_var_set, vm->var_set, sizeof(vm->var_set));
vm->ip = method->entry_point;
continue;
}
default: default:
break; break;
} }

View File

@@ -14,11 +14,12 @@ typedef enum {
TOK_STRING, // "hola" TOK_STRING, // "hola"
// Identificadores y keywords // Identificadores y keywords
TOK_ID, // x, foo, mi_var TOK_ID, // x, foo, mi_var
TOK_IF, // if TOK_IF, // if
TOK_WHILE, // while TOK_WHILE, // while
TOK_FN, // fn TOK_FN, // fn
TOK_RETURN, // return TOK_RETURN, // return
TOK_CLASS, // class
// Operadores // Operadores
TOK_ASSIGN, // = TOK_ASSIGN, // =
@@ -36,6 +37,7 @@ typedef enum {
TOK_RPAREN, // ) TOK_RPAREN, // )
TOK_COLON, // : TOK_COLON, // :
TOK_COMMA, // , TOK_COMMA, // ,
TOK_DOT, // .
TOK_NEWLINE, // \n (significativo, como en Python) TOK_NEWLINE, // \n (significativo, como en Python)
TOK_INDENT, // aumento de indentacion TOK_INDENT, // aumento de indentacion
TOK_DEDENT, // reduccion de indentacion TOK_DEDENT, // reduccion de indentacion
@@ -89,12 +91,17 @@ Token *tokenize(const char *source, int *token_count) {
pos++; pos++;
} }
int new_level = spaces / 4; // 4 espacios = 1 nivel int new_level = spaces / 4; // 4 espacios = 1 nivel
if (new_level > indent_level) { if (source[pos] != '\n' && source[pos] != '\0') {
tokens[count++] = make_token(TOK_INDENT, "INDENT"); if (new_level > indent_level) {
} else if (new_level < indent_level) { for (int l = indent_level; l < new_level; l++)
tokens[count++] = make_token(TOK_DEDENT, "DEDENT"); tokens[count++] = make_token(TOK_INDENT, "INDENT");
} else if (new_level < indent_level) {
for (int l = indent_level; l > new_level; l--)
tokens[count++] = make_token(TOK_DEDENT, "DEDENT");
}
indent_level = new_level;
} }
indent_level = new_level;
} else if (c == '+') { } else if (c == '+') {
tokens[count++] = make_token(TOK_PLUS, "+"); tokens[count++] = make_token(TOK_PLUS, "+");
pos++; pos++;
@@ -122,6 +129,9 @@ Token *tokenize(const char *source, int *token_count) {
} else if (c == ',') { } else if (c == ',') {
tokens[count++] = make_token(TOK_COMMA, ","); tokens[count++] = make_token(TOK_COMMA, ",");
pos++; pos++;
} else if (c == '.') {
tokens[count++] = make_token(TOK_DOT, ".");
pos++;
} else if (c == '(') { } else if (c == '(') {
tokens[count++] = make_token(TOK_LPAREN, "("); tokens[count++] = make_token(TOK_LPAREN, "(");
pos++; pos++;
@@ -142,10 +152,10 @@ Token *tokenize(const char *source, int *token_count) {
while (source[pos] >= '0' && source[pos] <= '9') while (source[pos] >= '0' && source[pos] <= '9')
pos++; pos++;
tokens[count++] = make_token(TOK_INT, substr(source, start, pos)); tokens[count++] = make_token(TOK_INT, substr(source, start, pos));
} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { } else if ((c >= 'a' && c <= 'z') || (c == '_') || (c >= 'A' && c <= 'Z')) {
// Leer todos los caracteres consecutivos // Leer todos los caracteres consecutivos
int start = pos; int start = pos;
while (isalnum(source[pos])) while (isalnum(source[pos]) || source[pos] == '_')
pos++; pos++;
char *word = substr(source, start, pos); char *word = substr(source, start, pos);
// tokens[count++] = make_token(TOK_ID, word); // tokens[count++] = make_token(TOK_ID, word);
@@ -159,6 +169,8 @@ Token *tokenize(const char *source, int *token_count) {
tokens[count++] = make_token(TOK_FN, word); tokens[count++] = make_token(TOK_FN, word);
} else if (strcmp(word, "return") == 0) { } else if (strcmp(word, "return") == 0) {
tokens[count++] = make_token(TOK_RETURN, word); tokens[count++] = make_token(TOK_RETURN, word);
} else if (strcmp(word, "class") == 0) {
tokens[count++] = make_token(TOK_CLASS, word);
} else { } else {
tokens[count++] = make_token(TOK_ID, word); tokens[count++] = make_token(TOK_ID, word);
} }

View File

@@ -18,7 +18,11 @@ typedef enum {
NODE_BLOCK, // secuencia de statements NODE_BLOCK, // secuencia de statements
NODE_CALL, NODE_CALL,
NODE_FN_DEF, // definicion de funcion NODE_FN_DEF, // definicion de funcion
NODE_RETURN, // return expr NODE_RETURN, // return expr
NODE_CLASS_DEF, // definicion de clase
NODE_DOT_ACCESS,
NODE_DOT_ASSIGN,
NODE_METHOD_CALL,
} NodeType; } NodeType;
typedef struct ASTNode { typedef struct ASTNode {
@@ -63,7 +67,27 @@ typedef struct ASTNode {
} fn_def; // NODE_FN_DEF } fn_def; // NODE_FN_DEF
struct { struct {
struct ASTNode *value; // expresion de retorno struct ASTNode *value; // expresion de retorno
} ret; // NODE_RETURN } ret; // NODE_RETURN
struct {
char *name;
struct ASTNode **methods;
int method_count;
} class_def;
struct {
struct ASTNode *object;
char *field;
} dot_access;
struct {
struct ASTNode *object;
char *field;
struct ASTNode *value;
} dot_assign;
struct {
struct ASTNode *object;
char *method;
struct ASTNode **args;
int arg_count;
} method_call;
} data; } data;
} ASTNode; } ASTNode;
@@ -91,6 +115,7 @@ ASTNode *parse_factor(Token *tokens) {
pos++; pos++;
return node; return node;
} else if (tokens[pos].type == TOK_ID) { } else if (tokens[pos].type == TOK_ID) {
ASTNode *node;
if (tokens[pos + 1].type == TOK_LPAREN) { if (tokens[pos + 1].type == TOK_LPAREN) {
// Function call // Function call
char *name = tokens[pos].value; char *name = tokens[pos].value;
@@ -110,15 +135,50 @@ ASTNode *parse_factor(Token *tokens) {
} }
} }
pos++; // consumir ")" pos++; // consumir ")"
ASTNode *node = make_node(NODE_CALL); node = make_node(NODE_CALL);
node->data.call.name = name; node->data.call.name = name;
node->data.call.args = args; node->data.call.args = args;
node->data.call.arg_count = arg_count; node->data.call.arg_count = arg_count;
return node; } else {
node = make_node(NODE_VAR);
node->data.string_val = tokens[pos].value;
pos++;
} }
ASTNode *node = make_node(NODE_VAR);
node->data.string_val = tokens[pos].value; // Loop post-fix para dot access
pos++; while (tokens[pos].type == TOK_DOT) {
pos++; // consumir .
char *field = tokens[pos].value;
pos++; // consumir field name
if (tokens[pos].type == TOK_LPAREN) {
// obj.method(args...)
pos++; // consumir '('
ASTNode **args = (ASTNode **)malloc(sizeof(ASTNode *) * 16);
int arg_count = 0;
if (tokens[pos].type != TOK_RPAREN) {
args[arg_count++] = parse_expr(tokens);
while (tokens[pos].type == TOK_COMMA) {
pos++;
args[arg_count++] = parse_expr(tokens);
}
}
pos++; // consumir ')'
ASTNode *mc = make_node(NODE_METHOD_CALL);
mc->data.method_call.object = node;
mc->data.method_call.method = field;
mc->data.method_call.args = args;
mc->data.method_call.arg_count = arg_count;
node = mc;
} else {
// obj.field
ASTNode *da = make_node(NODE_DOT_ACCESS);
da->data.dot_access.object = node;
da->data.dot_access.field = field;
node = da;
}
}
return node; return node;
} else if (tokens[pos].type == TOK_LPAREN) { } else if (tokens[pos].type == TOK_LPAREN) {
@@ -179,6 +239,24 @@ ASTNode *parse_term(Token *tokens) {
ASTNode *parse_statement(Token *tokens) { ASTNode *parse_statement(Token *tokens) {
if (tokens[pos].type == TOK_ID) { if (tokens[pos].type == TOK_ID) {
if (tokens[pos + 1].type == TOK_DOT) {
ASTNode *expr = parse_expr(tokens);
if (tokens[pos].type == TOK_ASSIGN) {
// dot_assign: self.name = expr
pos++; // consumir '='
ASTNode *value = parse_expr(tokens);
ASTNode *node = make_node(NODE_DOT_ASSIGN);
node->data.dot_assign.object = expr->data.dot_access.object;
node->data.dot_assign.field = expr->data.dot_access.field;
node->data.dot_assign.value = value;
return node;
}
// si no hay '=', es un method call como (d.speak())
return expr;
}
if (tokens[pos + 1].type == TOK_LPAREN) { if (tokens[pos + 1].type == TOK_LPAREN) {
// Es una funcion // Es una funcion
char *name = tokens[pos].value; char *name = tokens[pos].value;
@@ -246,7 +324,7 @@ ASTNode *parse_statement(Token *tokens) {
while (tokens[pos].type != TOK_DEDENT) { while (tokens[pos].type != TOK_DEDENT) {
body->data.block.stmts[body->data.block.count++] = body->data.block.stmts[body->data.block.count++] =
parse_statement(tokens); parse_statement(tokens);
if (tokens[pos].type == TOK_NEWLINE) { while (tokens[pos].type == TOK_NEWLINE) {
pos++; pos++;
} }
} }
@@ -272,7 +350,7 @@ ASTNode *parse_statement(Token *tokens) {
while (tokens[pos].type != TOK_DEDENT) { while (tokens[pos].type != TOK_DEDENT) {
body->data.block.stmts[body->data.block.count++] = body->data.block.stmts[body->data.block.count++] =
parse_statement(tokens); parse_statement(tokens);
if (tokens[pos].type == TOK_NEWLINE) { while (tokens[pos].type == TOK_NEWLINE) {
pos++; pos++;
} }
} }
@@ -284,9 +362,34 @@ ASTNode *parse_statement(Token *tokens) {
return node; return node;
} }
if (tokens[pos].type == TOK_CLASS) {
pos++; // consumir 'class'
char *name = tokens[pos].value;
pos++; // consumir nombre
pos++; // consumir :
pos++; // consumir NEWLINE
pos++; // consumir INDENT
ASTNode **methods = (ASTNode **)malloc(sizeof(ASTNode *) * 16);
int method_count = 0;
while (tokens[pos].type != TOK_DEDENT) {
methods[method_count++] = parse_statement(tokens);
while (tokens[pos].type == TOK_NEWLINE) {
pos++;
}
}
pos++; // consumir DEDENT
ASTNode *node = make_node(NODE_CLASS_DEF);
node->data.class_def.name = name;
node->data.class_def.methods = methods;
node->data.class_def.method_count = method_count;
return node;
}
if (tokens[pos].type == TOK_FN) { if (tokens[pos].type == TOK_FN) {
pos++; // consumir "fn" pos++; // consumir "fn"
char* name = tokens[pos].value; char *name = tokens[pos].value;
pos++; // consumir name pos++; // consumir name
pos++; // consumir "(" pos++; // consumir "("
@@ -314,13 +417,13 @@ ASTNode *parse_statement(Token *tokens) {
while (tokens[pos].type != TOK_DEDENT) { while (tokens[pos].type != TOK_DEDENT) {
body->data.block.stmts[body->data.block.count++] = body->data.block.stmts[body->data.block.count++] =
parse_statement(tokens); parse_statement(tokens);
if (tokens[pos].type == TOK_NEWLINE) { while (tokens[pos].type == TOK_NEWLINE) {
pos++; pos++;
} }
} }
pos++; // Consumir DEDENT pos++; // Consumir DEDENT
ASTNode*node = make_node(NODE_FN_DEF); ASTNode *node = make_node(NODE_FN_DEF);
node->data.fn_def.name = name; node->data.fn_def.name = name;
node->data.fn_def.params = params; node->data.fn_def.params = params;
node->data.fn_def.param_count = param_count; node->data.fn_def.param_count = param_count;
@@ -345,7 +448,7 @@ ASTNode *parse(Token *tokens, int token_count) {
block->data.block.count = 0; block->data.block.count = 0;
while (pos < token_count) { while (pos < token_count) {
if (tokens[pos].type == TOK_NEWLINE) { while (tokens[pos].type == TOK_NEWLINE) {
pos++; // Saltar newlines sueltos pos++; // Saltar newlines sueltos
continue; continue;
} }
@@ -443,6 +546,36 @@ void ast_print(ASTNode *node, const char *prefix, int is_last) {
} }
break; break;
case NODE_CLASS_DEF:
printf("NODE_CLASS_DEF(\"%s\")\n", node->data.class_def.name);
for (int i = 0; i < node->data.class_def.method_count; i++) {
ast_print(node->data.class_def.methods[i], new_prefix,
i == node->data.class_def.method_count - 1);
}
break;
case NODE_DOT_ACCESS:
printf("NODE_DOT_ACCESS(.%s)\n", node->data.dot_access.field);
ast_print(node->data.dot_access.object, new_prefix, 1);
break;
case NODE_DOT_ASSIGN:
printf("NODE_DOT_ASSIGN(.%s)\n", node->data.dot_assign.field);
ast_print(node->data.dot_assign.object, new_prefix, 0);
ast_print(node->data.dot_assign.value, new_prefix, 1);
break;
case NODE_METHOD_CALL:
printf("NODE_METHOD_CALL(.%s, %d args)\n", node->data.method_call.method,
node->data.method_call.arg_count);
ast_print(node->data.method_call.object, new_prefix,
node->data.method_call.arg_count == 0);
for (int i = 0; i < node->data.method_call.arg_count; i++) {
ast_print(node->data.method_call.args[i], new_prefix,
i == node->data.method_call.arg_count - 1);
}
break;
default: default:
printf("UNKNOWN\n"); printf("UNKNOWN\n");
break; break;

View File

@@ -4,6 +4,7 @@
#include "../objects/object.h" #include "../objects/object.h"
#include "allocator.h" #include "allocator.h"
void gc_collect(JLANG_memory_allocator *allocPtr, size_t *roots, void gc_collect(JLANG_memory_allocator *allocPtr, size_t *roots,
int root_count) { int root_count) {
// Stage 1. Mark blocks // Stage 1. Mark blocks
@@ -29,67 +30,79 @@ void gc_collect(JLANG_memory_allocator *allocPtr, size_t *roots,
objPtr->data.string_val.chars - objPtr->data.string_val.chars -
sizeof(JLANG_metadata)); sizeof(JLANG_metadata));
itemsHeader->marked = 1; itemsHeader->marked = 1;
} } else if (objPtr->type == OBJ_INSTANCE) {
} JLANG_metadata *namesHeader =
(JLANG_metadata *)((char *)allocPtr->memory +
objPtr->data.instance_val.field_names -
sizeof(JLANG_metadata));
namesHeader->marked = 1;
// Stage 2. Sweep memory JLANG_metadata *valuesHeader =
JLANG_metadata *currentHead = (JLANG_metadata *)allocPtr->memory; (JLANG_metadata *)((char *)allocPtr->memory +
while (currentHead->size != 0) { objPtr->data.instance_val.field_values -
size_t blockOffset = (char *)currentHead - (char *)allocPtr->memory; sizeof(JLANG_metadata));
valuesHeader->marked = 1;
if (currentHead->marked == 0 && currentHead->in_use) { }
// Free block
JLANG_free(allocPtr, blockOffset + sizeof(JLANG_metadata));
} }
// Reset mark // Stage 2. Sweep memory
currentHead->marked = 0; JLANG_metadata *currentHead = (JLANG_metadata *)allocPtr->memory;
while (currentHead->size != 0) {
size_t blockOffset = (char *)currentHead - (char *)allocPtr->memory;
currentHead = (JLANG_metadata *)((char *)currentHead + currentHead->size + if (currentHead->marked == 0 && currentHead->in_use) {
sizeof(JLANG_metadata)); // Free block
} JLANG_free(allocPtr, blockOffset + sizeof(JLANG_metadata));
}
// Stage 3. Join free blocks // Reset mark
currentHead = (JLANG_metadata *)allocPtr->memory; currentHead->marked = 0;
while (currentHead->size != 0) {
if (currentHead->in_use == 0) { currentHead = (JLANG_metadata *)((char *)currentHead + currentHead->size +
// iterate until last next free block sizeof(JLANG_metadata));
JLANG_metadata *nextHeader = }
(JLANG_metadata *)((char *)currentHead + sizeof(JLANG_metadata) +
currentHead->size); // Stage 3. Join free blocks
JLANG_metadata *lastHeader = currentHead; currentHead = (JLANG_metadata *)allocPtr->memory;
while (1) { while (currentHead->size != 0) {
if (nextHeader->in_use == 1 || nextHeader->size == 0) {
// Volvemos atrás if (currentHead->in_use == 0) {
break; // 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);
} }
lastHeader = nextHeader; // Make from currentHead to nextHeader + nextHeader(size) one block
nextHeader = size_t startIndex = (char *)currentHead - (char *)allocPtr->memory;
(JLANG_metadata *)((char *)nextHeader + sizeof(JLANG_metadata) + size_t endIndex =
nextHeader->size); ((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);
} }
// Make from currentHead to nextHeader + nextHeader(size) one block currentHead = (JLANG_metadata *)((char *)currentHead + currentHead->size +
size_t startIndex = (char *)currentHead - (char *)allocPtr->memory; sizeof(JLANG_metadata));
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 #endif

View File

@@ -3,11 +3,10 @@
#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)))
typedef enum { OBJ_INT, OBJ_FLOAT, OBJ_STRING, OBJ_LIST, OBJ_NONE } ObjectType; typedef enum { OBJ_INT, OBJ_FLOAT, OBJ_STRING, OBJ_LIST, OBJ_NONE, OBJ_INSTANCE } ObjectType;
typedef struct Object { typedef struct Object {
ObjectType type; ObjectType type;
@@ -23,9 +22,38 @@ typedef struct Object {
int count; int count;
int capacity; int capacity;
} list_val; } list_val;
struct {
int class_index; // indice en Chunk.classes[]
size_t field_names; // heap offset -> array de int
size_t field_values; // heap offset -> array de Value
int field_count;
int field_capacity;
} instance_val;
} data; } data;
} Object; } Object;
size_t obj_new_instance(void *allocator, int class_index, int capacity, size_t value_size) {
size_t offset = JLANG_malloc(allocator, sizeof(Object));
Object *objPtr = (Object *)JLANG_RESOLVE(allocator, offset);
objPtr->type = OBJ_INSTANCE;
objPtr->data.instance_val.class_index = class_index;
objPtr->data.instance_val.field_count = 0;
objPtr->data.instance_val.field_capacity = capacity;
// alloc array de nombres
size_t namesOffset = JLANG_malloc(allocator, capacity * sizeof(int));
objPtr = (Object *) JLANG_RESOLVE(allocator, offset); // re-resolve
// alloc array de valores
size_t valuesOffset = JLANG_malloc(allocator, capacity * value_size);
objPtr = (Object *) JLANG_RESOLVE(allocator, offset); // re-resolve
objPtr->data.instance_val.field_names = namesOffset;
objPtr->data.instance_val.field_values = valuesOffset;
return offset;
}
size_t obj_new_int(void *allocator, int value) { size_t obj_new_int(void *allocator, int value) {
// Object *objPtr = (Object *)JLANG_malloc(allocator, sizeof(Object)); // Object *objPtr = (Object *)JLANG_malloc(allocator, sizeof(Object));
size_t offset = JLANG_malloc(allocator, sizeof(Object)); size_t offset = JLANG_malloc(allocator, sizeof(Object));
@@ -91,10 +119,16 @@ void obj_free(void *allocator, size_t offset) {
JLANG_free(allocator, obj->data.list_val.items); JLANG_free(allocator, obj->data.list_val.items);
} }
if (obj->type == OBJ_INSTANCE) {
JLANG_free(allocator, obj->data.instance_val.field_names);
JLANG_free(allocator, obj->data.instance_val.field_values);
}
JLANG_free(allocator, offset); JLANG_free(allocator, offset);
} }
void obj_print(void *allocator, size_t offset, const char *preffix, const char *suffix) { void obj_print(void *allocator, size_t offset, const char *preffix,
const char *suffix) {
Object *obj = (Object *)JLANG_RESOLVE(allocator, offset); Object *obj = (Object *)JLANG_RESOLVE(allocator, offset);
switch (obj->type) { switch (obj->type) {