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.
This commit is contained in:
@@ -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")
|
||||
@@ -1,4 +1,5 @@
|
||||
x = 0
|
||||
while x < 1000000000:
|
||||
while x < 10000000:
|
||||
x = x + 1
|
||||
println(x)
|
||||
print(x)
|
||||
debugHeap()
|
||||
@@ -85,6 +85,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; i<n; i++){
|
||||
compile_node(chunk, node->data.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;
|
||||
}
|
||||
|
||||
@@ -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,8 +90,11 @@ 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];
|
||||
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);
|
||||
@@ -96,26 +102,37 @@ void run_vm(VM *vm) {
|
||||
case VAL_OBJ: {
|
||||
// Get object from heap
|
||||
obj_print(vm->allocator, v.as.heap_offset, "", "");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} 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;
|
||||
vm->sp -= nParams;
|
||||
|
||||
if (strcmp(name, "println") == 0) {
|
||||
printf("\n");
|
||||
}
|
||||
} 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};
|
||||
|
||||
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;
|
||||
@@ -208,6 +257,27 @@ void run_vm(VM *vm) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user