From da9bb6ca6250bd0bac9ef222387e0fc775c26500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Luis=20Monta=C3=B1es=20Ojados?= Date: Wed, 18 Feb 2026 02:26:44 +0100 Subject: [PATCH] Complete VM parity with eval and fix operator precedence VM: add string concatenation in OP_ADD, len() built-in, multi-arg print/println, undefined variable detection, and GC via OP_NOP. Parser: fix operator precedence by splitting into parse_expr (+,-) and parse_term (*,/) so 8 + 2 * 4 = 16 instead of 40. Compiler: emit OP_NOP at start of NODE_BLOCK to trigger GC. --- projects/vm_simple.j | 8 +- projects/while.j | 5 +- src/backend/bytecode/compiler.h | 4 + src/backend/bytecode/vm.h | 128 ++++++++++++++++++++++++-------- src/frontend/parser.h | 23 +++++- 5 files changed, 129 insertions(+), 39 deletions(-) diff --git a/projects/vm_simple.j b/projects/vm_simple.j index 8c29f72..588da65 100644 --- a/projects/vm_simple.j +++ b/projects/vm_simple.j @@ -1,6 +1,2 @@ -x = 0 -while x < 10: - x = x + 1 - -if x > 1000000: - println("OK") +x = 8 + 2 * 4 +print(x, end="\n") \ No newline at end of file diff --git a/projects/while.j b/projects/while.j index 6dc4169..2ddfa55 100644 --- a/projects/while.j +++ b/projects/while.j @@ -1,4 +1,5 @@ x = 0 -while x < 1000000000: +while x < 10000000: x = x + 1 -println(x) +print(x) +debugHeap() \ No newline at end of file diff --git a/src/backend/bytecode/compiler.h b/src/backend/bytecode/compiler.h index 697b620..06e9aa0 100644 --- a/src/backend/bytecode/compiler.h +++ b/src/backend/bytecode/compiler.h @@ -84,6 +84,9 @@ int compile_node(Chunk *chunk, ASTNode* node) { } case NODE_BLOCK: { int n = node->data.block.count; + + // NOP for gc + emit(chunk, make_instruction(OP_NOP)); for (int i=0; idata.block.stmts[i]); @@ -156,6 +159,7 @@ int compile_node(Chunk *chunk, ASTNode* node) { chunk->code[jump_offset].operand.jump_target = chunk->code_count; break; } + default: break; } diff --git a/src/backend/bytecode/vm.h b/src/backend/bytecode/vm.h index a0a7808..2a54002 100644 --- a/src/backend/bytecode/vm.h +++ b/src/backend/bytecode/vm.h @@ -5,7 +5,6 @@ #include "compiler.h" #include "value.h" - typedef struct { Chunk *chunk; int ip; // instruction pointer @@ -77,6 +76,10 @@ void run_vm(VM *vm) { case OP_LOAD_VAR: { // get from vm->var int idx = instr.operand.var_index; + if (!vm->var_set[idx]) { + printf("error: variable '%s' no definida\n", vm->chunk->names[idx]); + return; + } Value v = vm->vars[idx]; // push to stack vm->stack[vm->sp++] = v; @@ -87,35 +90,49 @@ void run_vm(VM *vm) { int nameIdx = instr.operand.call.name_index; char *name = vm->chunk->names[nameIdx]; - if (strcmp(name, "print") == 0) { - Value v = vm->stack[--vm->sp]; - switch (v.type) { - case VAL_INT: - printf("%d", v.as.int_val); - break; - case VAL_OBJ: { - // Get object from heap - obj_print(vm->allocator, v.as.heap_offset, "", ""); + if (strcmp(name, "print") == 0 || strcmp(name, "println") == 0) { + + int nParams = instr.operand.call.arg_count; + for (int i = 0; i < nParams; i++) { + Value v = vm->stack[vm->sp - nParams + i]; + switch (v.type) { + case VAL_INT: + printf("%d", v.as.int_val); + break; + case VAL_OBJ: { + // Get object from heap + obj_print(vm->allocator, v.as.heap_offset, "", ""); + break; + } + default: + break; + } } - default: - break; + vm->sp -= nParams; + + if (strcmp(name, "println") == 0) { + printf("\n"); } - } else if (strcmp(name, "println") == 0) { - Value v = vm->stack[--vm->sp]; - switch (v.type) { - case VAL_INT: - printf("%d\n", v.as.int_val); - break; - case VAL_OBJ: { - // Get object from heap - obj_print(vm->allocator, v.as.heap_offset, "", "\n"); - } - default: - break; - } - } else if (strcmp(name, "debug_heap") == 0) { + + } else if (strcmp(name, "debugHeap") == 0) { + printf("\n"); JLANG_visualize(vm->allocator); break; + } else if (strcmp(name, "len") == 0) { + // pop value from stack + Value var1 = vm->stack[--vm->sp]; + if (var1.type == VAL_OBJ) { + // Resolve obj + Object *obj = JLANG_RESOLVE(vm->allocator, var1.as.heap_offset); + + Value result = {0}; + result.type = VAL_INT; + result.as.int_val = obj->data.string_val.length; + + // push to stack + vm->stack[vm->sp++] = result; + } + break; } else { printf("error: function '%s' not found!\n", name); return; @@ -129,9 +146,41 @@ void run_vm(VM *vm) { Value var2 = vm->stack[--vm->sp]; Value var1 = vm->stack[--vm->sp]; + if (var1.type != var2.type) { + printf("panic: var types mismatch on OP_ADD\n"); + return; + } + Value result = {0}; - result.type = VAL_INT; - result.as.int_val = var1.as.int_val + var2.as.int_val; + + if (var1.type == VAL_INT) { + result.type = VAL_INT; + result.as.int_val = var1.as.int_val + var2.as.int_val; + } else if (var1.type == VAL_OBJ) { + // resolve obj + Object *obj1 = JLANG_RESOLVE(vm->allocator, var1.as.heap_offset); + Object *obj2 = JLANG_RESOLVE(vm->allocator, var2.as.heap_offset); + + // get chars + char *str1 = JLANG_RESOLVE(vm->allocator, obj1->data.string_val.chars); + char *str2 = JLANG_RESOLVE(vm->allocator, obj2->data.string_val.chars); + + // tmp char buffer + size_t total = + obj1->data.string_val.length + obj2->data.string_val.length; + char *tmpBuffer = (char *)malloc(total + 1); + memcpy(tmpBuffer, str1, obj1->data.string_val.length); + memcpy(tmpBuffer + obj1->data.string_val.length, str2, + obj2->data.string_val.length); + tmpBuffer[total] = '\0'; + + // Create new str + size_t strHeapIndex = obj_new_string(vm->allocator, tmpBuffer); + free(tmpBuffer); + // set value + result.type = VAL_OBJ; + result.as.heap_offset = strHeapIndex; + } // Push to stack vm->stack[vm->sp++] = result; @@ -207,7 +256,28 @@ void run_vm(VM *vm) { vm->stack[vm->sp++] = result; break; } - + + case OP_NOP: { + // Pass gc + size_t roots[512]; + int root_count = 0; + + for (int i = 0; i < 256; i++) { + if (vm->var_set[i] && vm->vars[i].type == VAL_OBJ) { + roots[root_count++] = vm->vars[i].as.heap_offset; + } + } + + for (int i = 0; i < vm->sp; i++) { + if (vm->stack[i].type == VAL_OBJ) { + roots[root_count++] = vm->stack[i].as.heap_offset; + } + } + + gc_collect(vm->allocator, roots, root_count); + + break; + } default: break; } diff --git a/src/frontend/parser.h b/src/frontend/parser.h index 2dd2812..f9fc03c 100644 --- a/src/frontend/parser.h +++ b/src/frontend/parser.h @@ -65,8 +65,11 @@ ASTNode *make_node(NodeType type) { int pos = 0; ASTNode *parse_expr(Token *tokens); +ASTNode *parse_term(Token *tokens); +ASTNode *parse_factor(Token *tokens); -ASTNode *parse_term(Token *tokens) { + +ASTNode *parse_factor(Token *tokens) { if (tokens[pos].type == TOK_INT) { ASTNode *node = make_node(NODE_INT_LIT); node->data.int_val = atoi(tokens[pos].value); @@ -133,7 +136,6 @@ 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++; @@ -148,6 +150,23 @@ ASTNode *parse_expr(Token *tokens) { return left; } +ASTNode *parse_term(Token *tokens) { + ASTNode *left = parse_factor(tokens); + + while (tokens[pos].type == TOK_STAR || tokens[pos].type == TOK_SLASH) { + char op = tokens[pos].value[0]; + pos++; + ASTNode *right = parse_factor(tokens); + + ASTNode *binop = make_node(NODE_BINOP); + binop->data.binop.op = op; + binop->data.binop.left = left; + binop->data.binop.right = right; + left = binop; + } + return left; +} + ASTNode *parse_statement(Token *tokens) { if (tokens[pos].type == TOK_ID) { if (tokens[pos + 1].type == TOK_LPAREN) {