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
|
x = 8 + 2 * 4
|
||||||
while x < 10:
|
print(x, end="\n")
|
||||||
x = x + 1
|
|
||||||
|
|
||||||
if x > 1000000:
|
|
||||||
println("OK")
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
x = 0
|
x = 0
|
||||||
while x < 1000000000:
|
while x < 10000000:
|
||||||
x = x + 1
|
x = x + 1
|
||||||
println(x)
|
print(x)
|
||||||
|
debugHeap()
|
||||||
@@ -85,6 +85,9 @@ int compile_node(Chunk *chunk, ASTNode* node) {
|
|||||||
case NODE_BLOCK: {
|
case NODE_BLOCK: {
|
||||||
int n = node->data.block.count;
|
int n = node->data.block.count;
|
||||||
|
|
||||||
|
// NOP for gc
|
||||||
|
emit(chunk, make_instruction(OP_NOP));
|
||||||
|
|
||||||
for (int i=0; i<n; i++){
|
for (int i=0; i<n; i++){
|
||||||
compile_node(chunk, node->data.block.stmts[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;
|
chunk->code[jump_offset].operand.jump_target = chunk->code_count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "compiler.h"
|
#include "compiler.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Chunk *chunk;
|
Chunk *chunk;
|
||||||
int ip; // instruction pointer
|
int ip; // instruction pointer
|
||||||
@@ -77,6 +76,10 @@ void run_vm(VM *vm) {
|
|||||||
case OP_LOAD_VAR: {
|
case OP_LOAD_VAR: {
|
||||||
// get from vm->var
|
// get from vm->var
|
||||||
int idx = instr.operand.var_index;
|
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];
|
Value v = vm->vars[idx];
|
||||||
// push to stack
|
// push to stack
|
||||||
vm->stack[vm->sp++] = v;
|
vm->stack[vm->sp++] = v;
|
||||||
@@ -87,35 +90,49 @@ void run_vm(VM *vm) {
|
|||||||
int nameIdx = instr.operand.call.name_index;
|
int nameIdx = instr.operand.call.name_index;
|
||||||
|
|
||||||
char *name = vm->chunk->names[nameIdx];
|
char *name = vm->chunk->names[nameIdx];
|
||||||
if (strcmp(name, "print") == 0) {
|
if (strcmp(name, "print") == 0 || strcmp(name, "println") == 0) {
|
||||||
Value v = vm->stack[--vm->sp];
|
|
||||||
switch (v.type) {
|
int nParams = instr.operand.call.arg_count;
|
||||||
case VAL_INT:
|
for (int i = 0; i < nParams; i++) {
|
||||||
printf("%d", v.as.int_val);
|
Value v = vm->stack[vm->sp - nParams + i];
|
||||||
break;
|
switch (v.type) {
|
||||||
case VAL_OBJ: {
|
case VAL_INT:
|
||||||
// Get object from heap
|
printf("%d", v.as.int_val);
|
||||||
obj_print(vm->allocator, v.as.heap_offset, "", "");
|
break;
|
||||||
|
case VAL_OBJ: {
|
||||||
|
// Get object from heap
|
||||||
|
obj_print(vm->allocator, v.as.heap_offset, "", "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
vm->sp -= nParams;
|
||||||
break;
|
|
||||||
|
if (strcmp(name, "println") == 0) {
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
} else if (strcmp(name, "println") == 0) {
|
|
||||||
Value v = vm->stack[--vm->sp];
|
} else if (strcmp(name, "debugHeap") == 0) {
|
||||||
switch (v.type) {
|
printf("\n");
|
||||||
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) {
|
|
||||||
JLANG_visualize(vm->allocator);
|
JLANG_visualize(vm->allocator);
|
||||||
break;
|
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 {
|
} else {
|
||||||
printf("error: function '%s' not found!\n", name);
|
printf("error: function '%s' not found!\n", name);
|
||||||
return;
|
return;
|
||||||
@@ -129,9 +146,41 @@ void run_vm(VM *vm) {
|
|||||||
Value var2 = vm->stack[--vm->sp];
|
Value var2 = vm->stack[--vm->sp];
|
||||||
Value var1 = 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};
|
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
|
// Push to stack
|
||||||
vm->stack[vm->sp++] = result;
|
vm->stack[vm->sp++] = result;
|
||||||
@@ -208,6 +257,27 @@ void run_vm(VM *vm) {
|
|||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,11 @@ ASTNode *make_node(NodeType type) {
|
|||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
ASTNode *parse_expr(Token *tokens);
|
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) {
|
if (tokens[pos].type == TOK_INT) {
|
||||||
ASTNode *node = make_node(NODE_INT_LIT);
|
ASTNode *node = make_node(NODE_INT_LIT);
|
||||||
node->data.int_val = atoi(tokens[pos].value);
|
node->data.int_val = atoi(tokens[pos].value);
|
||||||
@@ -133,7 +136,6 @@ ASTNode *parse_expr(Token *tokens) {
|
|||||||
ASTNode *left = parse_term(tokens);
|
ASTNode *left = parse_term(tokens);
|
||||||
|
|
||||||
while (tokens[pos].type == TOK_PLUS || tokens[pos].type == TOK_MINUS ||
|
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) {
|
tokens[pos].type == TOK_LT || tokens[pos].type == TOK_GT) {
|
||||||
char op = tokens[pos].value[0]; // +,-,*,/
|
char op = tokens[pos].value[0]; // +,-,*,/
|
||||||
pos++;
|
pos++;
|
||||||
@@ -148,6 +150,23 @@ ASTNode *parse_expr(Token *tokens) {
|
|||||||
return left;
|
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) {
|
ASTNode *parse_statement(Token *tokens) {
|
||||||
if (tokens[pos].type == TOK_ID) {
|
if (tokens[pos].type == TOK_ID) {
|
||||||
if (tokens[pos + 1].type == TOK_LPAREN) {
|
if (tokens[pos + 1].type == TOK_LPAREN) {
|
||||||
|
|||||||
Reference in New Issue
Block a user