Functions.
This commit is contained in:
parent
3f21434cd1
commit
0a49a463e2
8 changed files with 312 additions and 29 deletions
|
|
@ -20,7 +20,6 @@ target_include_directories(fun_core PUBLIC
|
||||||
# Playground / interpreter (future /usr/bin/fun)
|
# Playground / interpreter (future /usr/bin/fun)
|
||||||
add_executable(fun
|
add_executable(fun
|
||||||
src/fun.c
|
src/fun.c
|
||||||
examples/hello_world.fun
|
|
||||||
)
|
)
|
||||||
target_link_libraries(fun PRIVATE fun_core)
|
target_link_libraries(fun PRIVATE fun_core)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ print("=== Expressions test start ===")
|
||||||
number a = 2
|
number a = 2
|
||||||
number b = 3
|
number b = 3
|
||||||
number n = 10
|
number n = 10
|
||||||
string s = "Hello, world!"
|
string s = "Have, fun!"
|
||||||
boolean flag = true
|
boolean flag = true
|
||||||
|
|
||||||
// Arithmetic and precedence
|
// Arithmetic and precedence
|
||||||
|
|
|
||||||
40
examples/functions_test.fun
Executable file
40
examples/functions_test.fun
Executable file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
// Functions test: definitions, calls, parameters, locals, returns, and if/else.
|
||||||
|
|
||||||
|
print("=== Functions test start ===")
|
||||||
|
|
||||||
|
fun add(a, b)
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
fun abs(x)
|
||||||
|
if (x < 0)
|
||||||
|
return -x
|
||||||
|
else
|
||||||
|
return x
|
||||||
|
|
||||||
|
fun sum3(a, b, c)
|
||||||
|
// local typed declaration
|
||||||
|
number t = a + b
|
||||||
|
return t + c
|
||||||
|
|
||||||
|
fun id(x)
|
||||||
|
return x
|
||||||
|
|
||||||
|
// Calls in expressions
|
||||||
|
print(add(2, 3)) // expect 5
|
||||||
|
print(add(10, -3)) // expect 7
|
||||||
|
print(abs(-7)) // expect 7
|
||||||
|
print(abs(4)) // expect 4
|
||||||
|
print(sum3(1, 2, 3)) // expect 6
|
||||||
|
print(add(add(1, 2), 3)) // expect 6
|
||||||
|
print(id(42)) // expect 42
|
||||||
|
|
||||||
|
// Call as statement (result discarded)
|
||||||
|
add(100, 23)
|
||||||
|
|
||||||
|
// Globals interacting with functions
|
||||||
|
number n = 5
|
||||||
|
print(add(n, 10)) // expect 15
|
||||||
|
|
||||||
|
print("=== Functions test end ===")
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env fun
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Have fun! in Fun
|
* Have fun in Fun
|
||||||
*/
|
*/
|
||||||
|
|
||||||
print("Hello, fun!")
|
print("Have, fun!")
|
||||||
|
|
|
||||||
10
examples/have_fun_function.fun
Executable file
10
examples/have_fun_function.fun
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Have fun in Fun
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun have_fun()
|
||||||
|
print("Have, fun!")
|
||||||
|
|
||||||
|
have_fun()
|
||||||
5
play.fun
5
play.fun
|
|
@ -8,3 +8,8 @@ print(n)
|
||||||
string s = "Hello, world!"
|
string s = "Hello, world!"
|
||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
|
fun foo()
|
||||||
|
//print("Have fun!")
|
||||||
|
print("Running foo()... forever.")
|
||||||
|
|
||||||
|
foo()
|
||||||
|
|
|
||||||
241
src/parser.c
241
src/parser.c
|
|
@ -217,6 +217,33 @@ static int sym_index(const char *name) {
|
||||||
return G.count++;
|
return G.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- locals environment for functions ---- */
|
||||||
|
typedef struct {
|
||||||
|
char *names[FRAME_MAX_LOCALS];
|
||||||
|
int count;
|
||||||
|
} LocalEnv;
|
||||||
|
|
||||||
|
static LocalEnv *g_locals = NULL;
|
||||||
|
|
||||||
|
static int local_find(const char *name) {
|
||||||
|
if (!g_locals) return -1;
|
||||||
|
for (int i = 0; i < g_locals->count; ++i) {
|
||||||
|
if (strcmp(g_locals->names[i], name) == 0) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int local_add(const char *name) {
|
||||||
|
if (!g_locals) return -1;
|
||||||
|
if (g_locals->count >= FRAME_MAX_LOCALS) {
|
||||||
|
parser_fail(0, "Too many local variables/parameters (max %d)", FRAME_MAX_LOCALS);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int idx = g_locals->count++;
|
||||||
|
g_locals->names[idx] = strdup(name);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
/* forward declaration so helpers can recurse */
|
/* forward declaration so helpers can recurse */
|
||||||
static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *pos);
|
static int emit_expression(Bytecode *bc, const char *src, size_t len, size_t *pos);
|
||||||
|
|
||||||
|
|
@ -267,10 +294,55 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* call or variable load (locals preferred) */
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
int local_idx = local_find(name);
|
||||||
|
int is_call = (*pos < len && src[*pos] == '(');
|
||||||
|
|
||||||
|
if (is_call) {
|
||||||
|
/* push function value first */
|
||||||
|
if (local_idx >= 0) {
|
||||||
|
bytecode_add_instruction(bc, OP_LOAD_LOCAL, local_idx);
|
||||||
|
} else {
|
||||||
int gi = sym_index(name);
|
int gi = sym_index(name);
|
||||||
free(name);
|
|
||||||
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||||
|
}
|
||||||
|
/* parse arguments */
|
||||||
|
(*pos)++; /* '(' */
|
||||||
|
int argc = 0;
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] != ')') {
|
||||||
|
do {
|
||||||
|
if (!emit_expression(bc, src, len, pos)) {
|
||||||
|
parser_fail(*pos, "Expected expression as function argument");
|
||||||
|
free(name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
argc++;
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
} while (*pos < len && src[*pos] == ',' && (++(*pos), skip_spaces(src, len, pos), 1));
|
||||||
|
}
|
||||||
|
if (!consume_char(src, len, pos, ')')) {
|
||||||
|
parser_fail(*pos, "Expected ')' after arguments");
|
||||||
|
free(name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* DEBUG: show compiled call */
|
||||||
|
printf("compile: CALL %s with %d arg(s)\n", name, argc);
|
||||||
|
bytecode_add_instruction(bc, OP_CALL, argc);
|
||||||
|
free(name);
|
||||||
return 1;
|
return 1;
|
||||||
|
} else {
|
||||||
|
if (local_idx >= 0) {
|
||||||
|
bytecode_add_instruction(bc, OP_LOAD_LOCAL, local_idx);
|
||||||
|
} else {
|
||||||
|
int gi = sym_index(name);
|
||||||
|
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||||
|
}
|
||||||
|
free(name);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -472,7 +544,7 @@ static int read_line_start(const char *src, size_t len, size_t *pos, int *out_in
|
||||||
if (p >= len) { *pos = p; return 0; }
|
if (p >= len) { *pos = p; return 0; }
|
||||||
if (src[p] == '\n') { /* empty line */ p++; *pos = p; continue; }
|
if (src[p] == '\n') { /* empty line */ p++; *pos = p; continue; }
|
||||||
|
|
||||||
/* comment-only line? */
|
/* // comment-only line */
|
||||||
if (p + 1 < len && src[p] == '/' && src[p + 1] == '/') {
|
if (p + 1 < len && src[p] == '/' && src[p + 1] == '/') {
|
||||||
/* skip entire line */
|
/* skip entire line */
|
||||||
while (p < len && src[p] != '\n') p++;
|
while (p < len && src[p] != '\n') p++;
|
||||||
|
|
@ -481,6 +553,21 @@ static int read_line_start(const char *src, size_t len, size_t *pos, int *out_in
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// block comment starting at line (treat as comment-only line)
|
||||||
|
if (p + 1 < len && src[p] == '/' && src[p + 1] == '*') {
|
||||||
|
p += 2;
|
||||||
|
/* advance until we find closing block comment marker */
|
||||||
|
while (p + 1 < len && !(src[p] == '*' && src[p + 1] == '/')) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (p + 1 < len) p += 2; /* consume closing block comment marker */
|
||||||
|
/* consume to end of current line (if any leftover) */
|
||||||
|
while (p < len && src[p] != '\n') p++;
|
||||||
|
if (p < len && src[p] == '\n') p++;
|
||||||
|
*pos = p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (spaces % 2 != 0) {
|
if (spaces % 2 != 0) {
|
||||||
parser_fail(p, "Indentation must be multiples of two spaces");
|
parser_fail(p, "Indentation must be multiples of two spaces");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -500,6 +587,26 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
||||||
size_t local_pos = *pos;
|
size_t local_pos = *pos;
|
||||||
char *name = NULL;
|
char *name = NULL;
|
||||||
if (read_identifier_into(src, len, &local_pos, &name)) {
|
if (read_identifier_into(src, len, &local_pos, &name)) {
|
||||||
|
/* return statement */
|
||||||
|
if (strcmp(name, "return") == 0) {
|
||||||
|
free(name);
|
||||||
|
skip_spaces(src, len, &local_pos);
|
||||||
|
/* optional expression */
|
||||||
|
size_t save_pos = local_pos;
|
||||||
|
if (emit_expression(bc, src, len, &local_pos)) {
|
||||||
|
/* expression result already on stack */
|
||||||
|
} else {
|
||||||
|
/* no expression: return nil */
|
||||||
|
local_pos = save_pos;
|
||||||
|
int ci = bytecode_add_constant(bc, make_nil());
|
||||||
|
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||||
|
}
|
||||||
|
bytecode_add_instruction(bc, OP_RETURN, 0);
|
||||||
|
*pos = local_pos;
|
||||||
|
skip_to_eol(src, len, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* typed declarations: number|string|boolean <ident> (= expr)? */
|
/* typed declarations: number|string|boolean <ident> (= expr)? */
|
||||||
if (strcmp(name, "number") == 0 || strcmp(name, "string") == 0 || strcmp(name, "boolean") == 0) {
|
if (strcmp(name, "number") == 0 || strcmp(name, "string") == 0 || strcmp(name, "boolean") == 0) {
|
||||||
int is_number = (strcmp(name, "number") == 0);
|
int is_number = (strcmp(name, "number") == 0);
|
||||||
|
|
@ -514,18 +621,31 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
||||||
parser_fail(local_pos, "Expected identifier after type declaration");
|
parser_fail(local_pos, "Expected identifier after type declaration");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int gi = sym_index(varname);
|
|
||||||
|
/* decide local vs global */
|
||||||
|
int lidx = -1;
|
||||||
|
int gi = -1;
|
||||||
|
if (g_locals) {
|
||||||
|
int existing = local_find(varname);
|
||||||
|
if (existing >= 0) lidx = existing;
|
||||||
|
else lidx = local_add(varname);
|
||||||
|
} else {
|
||||||
|
gi = sym_index(varname);
|
||||||
|
}
|
||||||
free(varname);
|
free(varname);
|
||||||
|
|
||||||
skip_spaces(src, len, &local_pos);
|
skip_spaces(src, len, &local_pos);
|
||||||
if (local_pos < len && src[local_pos] == '=') {
|
if (local_pos < len && src[local_pos] == '=') {
|
||||||
local_pos++; /* '=' */
|
local_pos++; /* '=' */
|
||||||
if (emit_expression(bc, src, len, &local_pos)) {
|
if (!emit_expression(bc, src, len, &local_pos)) {
|
||||||
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
|
|
||||||
} else {
|
|
||||||
parser_fail(local_pos, "Expected initializer expression after '='");
|
parser_fail(local_pos, "Expected initializer expression after '='");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (lidx >= 0) {
|
||||||
|
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
|
||||||
|
} else {
|
||||||
|
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* default initialize if no '=' given */
|
/* default initialize if no '=' given */
|
||||||
int ci = -1;
|
int ci = -1;
|
||||||
|
|
@ -536,9 +656,13 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
||||||
}
|
}
|
||||||
if (ci >= 0) {
|
if (ci >= 0) {
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||||
|
if (lidx >= 0) {
|
||||||
|
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
|
||||||
|
} else {
|
||||||
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
|
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
*pos = local_pos;
|
*pos = local_pos;
|
||||||
skip_to_eol(src, len, pos);
|
skip_to_eol(src, len, pos);
|
||||||
return;
|
return;
|
||||||
|
|
@ -572,10 +696,12 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
||||||
skip_to_eol(src, len, pos);
|
skip_to_eol(src, len, pos);
|
||||||
return;
|
return;
|
||||||
} else if (local_pos < len && src[local_pos] == '(') {
|
} else if (local_pos < len && src[local_pos] == '(') {
|
||||||
/* simple call form (argument ignored/emitted but no CALL yet) */
|
/* call as a statement: compile full call expression and drop result */
|
||||||
local_pos++; /* '(' */
|
/* rewind to statement start and emit as expression */
|
||||||
(void)emit_expression(bc, src, len, &local_pos);
|
local_pos = *pos;
|
||||||
(void)consume_char(src, len, &local_pos, ')');
|
if (emit_expression(bc, src, len, &local_pos)) {
|
||||||
|
bytecode_add_instruction(bc, OP_POP, 0); /* discard return value */
|
||||||
|
}
|
||||||
*pos = local_pos;
|
*pos = local_pos;
|
||||||
skip_to_eol(src, len, pos);
|
skip_to_eol(src, len, pos);
|
||||||
return;
|
return;
|
||||||
|
|
@ -628,6 +754,91 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* at same indent -> parse statement */
|
/* at same indent -> parse statement */
|
||||||
|
if (starts_with(src, len, *pos, "fun")) {
|
||||||
|
/* parse header: fun name(arg, ...) */
|
||||||
|
*pos += 3;
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
char *fname = NULL;
|
||||||
|
if (!read_identifier_into(src, len, pos, &fname)) {
|
||||||
|
parser_fail(*pos, "Expected function name after 'fun'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int fgi = sym_index(fname);
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (!consume_char(src, len, pos, '(')) {
|
||||||
|
parser_fail(*pos, "Expected '(' after function name");
|
||||||
|
free(fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build locals from parameters */
|
||||||
|
LocalEnv env = { {0}, 0 };
|
||||||
|
LocalEnv *prev = g_locals;
|
||||||
|
g_locals = &env;
|
||||||
|
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] != ')') {
|
||||||
|
for (;;) {
|
||||||
|
char *pname = NULL;
|
||||||
|
if (!read_identifier_into(src, len, pos, &pname)) {
|
||||||
|
parser_fail(*pos, "Expected parameter name");
|
||||||
|
g_locals = prev;
|
||||||
|
free(fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (local_find(pname) >= 0) {
|
||||||
|
parser_fail(*pos, "Duplicate parameter name '%s'", pname);
|
||||||
|
free(pname);
|
||||||
|
g_locals = prev;
|
||||||
|
free(fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
local_add(pname);
|
||||||
|
free(pname);
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == ',') { (*pos)++; skip_spaces(src, len, pos); continue; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!consume_char(src, len, pos, ')')) {
|
||||||
|
parser_fail(*pos, "Expected ')' after parameter list");
|
||||||
|
g_locals = prev;
|
||||||
|
free(fname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* end header line */
|
||||||
|
skip_to_eol(src, len, pos);
|
||||||
|
|
||||||
|
/* compile body into separate Bytecode */
|
||||||
|
Bytecode *fn_bc = bytecode_new();
|
||||||
|
|
||||||
|
/* parse body at increased indent if present */
|
||||||
|
int body_indent = 0;
|
||||||
|
size_t look_body = *pos;
|
||||||
|
if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
|
||||||
|
/* do not advance pos here; let parse_block consume the line */
|
||||||
|
parse_block(fn_bc, src, len, pos, body_indent);
|
||||||
|
} else {
|
||||||
|
/* empty body: ok */
|
||||||
|
}
|
||||||
|
/* ensure function returns */
|
||||||
|
bytecode_add_instruction(fn_bc, OP_RETURN, 0);
|
||||||
|
|
||||||
|
/* DEBUG: dump compiled function bytecode */
|
||||||
|
printf("=== compiled function %s (%d params) ===\n", fname, env.count);
|
||||||
|
bytecode_dump(fn_bc);
|
||||||
|
printf("=== end function %s ===\n", fname);
|
||||||
|
|
||||||
|
/* bind function to global: LOAD_CONST <fn> ; STORE_GLOBAL fgi */
|
||||||
|
int fci = bytecode_add_constant(bc, make_function(fn_bc));
|
||||||
|
bytecode_add_instruction(bc, OP_LOAD_CONST, fci);
|
||||||
|
bytecode_add_instruction(bc, OP_STORE_GLOBAL, fgi);
|
||||||
|
|
||||||
|
g_locals = prev;
|
||||||
|
free(fname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (starts_with(src, len, *pos, "if")) {
|
if (starts_with(src, len, *pos, "if")) {
|
||||||
int end_jumps[64];
|
int end_jumps[64];
|
||||||
int end_count = 0;
|
int end_count = 0;
|
||||||
|
|
@ -655,10 +866,10 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
|
|
||||||
/* parse nested block if next line is indented */
|
/* parse nested block if next line is indented */
|
||||||
int next_indent = 0;
|
int next_indent = 0;
|
||||||
size_t after_hdr = *pos;
|
size_t look_next = *pos;
|
||||||
if (read_line_start(src, len, pos, &next_indent)) {
|
if (read_line_start(src, len, &look_next, &next_indent)) {
|
||||||
if (next_indent > current_indent) {
|
if (next_indent > current_indent) {
|
||||||
/* parse body at increased indent */
|
/* parse body at increased indent (let parse_block consume the line) */
|
||||||
parse_block(bc, src, len, pos, next_indent);
|
parse_block(bc, src, len, pos, next_indent);
|
||||||
} else {
|
} else {
|
||||||
/* empty body; keep *pos at start of that line */
|
/* empty body; keep *pos at start of that line */
|
||||||
|
|
@ -703,7 +914,9 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
/* plain else: parse its block and finish the chain */
|
/* plain else: parse its block and finish the chain */
|
||||||
skip_to_eol(src, len, pos);
|
skip_to_eol(src, len, pos);
|
||||||
int else_indent = 0;
|
int else_indent = 0;
|
||||||
if (read_line_start(src, len, pos, &else_indent) && else_indent > current_indent) {
|
size_t look_else = *pos;
|
||||||
|
if (read_line_start(src, len, &look_else, &else_indent) && else_indent > current_indent) {
|
||||||
|
/* let parse_block consume the line */
|
||||||
parse_block(bc, src, len, pos, else_indent);
|
parse_block(bc, src, len, pos, else_indent);
|
||||||
} else {
|
} else {
|
||||||
/* empty else-body */
|
/* empty else-body */
|
||||||
|
|
|
||||||
28
src/vm.c
28
src/vm.c
|
|
@ -5,6 +5,16 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char* value_type_name(ValueType t) {
|
||||||
|
switch (t) {
|
||||||
|
case VAL_INT: return "int";
|
||||||
|
case VAL_STRING: return "string";
|
||||||
|
case VAL_FUNCTION: return "function";
|
||||||
|
case VAL_NIL: return "nil";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void vm_clear_output(VM *vm) {
|
void vm_clear_output(VM *vm) {
|
||||||
for (int i = 0; i < vm->output_count; ++i) {
|
for (int i = 0; i < vm->output_count; ++i) {
|
||||||
free_value(vm->output[i]);
|
free_value(vm->output[i]);
|
||||||
|
|
@ -144,7 +154,8 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
Value b = pop_value(vm);
|
Value b = pop_value(vm);
|
||||||
Value a = pop_value(vm);
|
Value a = pop_value(vm);
|
||||||
if (a.type != VAL_INT || b.type != VAL_INT) {
|
if (a.type != VAL_INT || b.type != VAL_INT) {
|
||||||
fprintf(stderr, "Runtime type error: ADD expects ints\n");
|
fprintf(stderr, "Runtime type error: ADD expects ints, got %s and %s\n",
|
||||||
|
value_type_name(a.type), value_type_name(b.type));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Value res = make_int(a.i + b.i);
|
Value res = make_int(a.i + b.i);
|
||||||
|
|
@ -158,7 +169,8 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
Value b = pop_value(vm);
|
Value b = pop_value(vm);
|
||||||
Value a = pop_value(vm);
|
Value a = pop_value(vm);
|
||||||
if (a.type != VAL_INT || b.type != VAL_INT) {
|
if (a.type != VAL_INT || b.type != VAL_INT) {
|
||||||
fprintf(stderr, "Runtime type error: SUB expects ints\n");
|
fprintf(stderr, "Runtime type error: SUB expects ints, got %s and %s\n",
|
||||||
|
value_type_name(a.type), value_type_name(b.type));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Value res = make_int(a.i - b.i);
|
Value res = make_int(a.i - b.i);
|
||||||
|
|
@ -172,7 +184,8 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
Value b = pop_value(vm);
|
Value b = pop_value(vm);
|
||||||
Value a = pop_value(vm);
|
Value a = pop_value(vm);
|
||||||
if (a.type != VAL_INT || b.type != VAL_INT) {
|
if (a.type != VAL_INT || b.type != VAL_INT) {
|
||||||
fprintf(stderr, "Runtime type error: MUL expects ints\n");
|
fprintf(stderr, "Runtime type error: MUL expects ints, got %s and %s\n",
|
||||||
|
value_type_name(a.type), value_type_name(b.type));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Value res = make_int(a.i * b.i);
|
Value res = make_int(a.i * b.i);
|
||||||
|
|
@ -186,7 +199,8 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
Value b = pop_value(vm);
|
Value b = pop_value(vm);
|
||||||
Value a = pop_value(vm);
|
Value a = pop_value(vm);
|
||||||
if (a.type != VAL_INT || b.type != VAL_INT) {
|
if (a.type != VAL_INT || b.type != VAL_INT) {
|
||||||
fprintf(stderr, "Runtime type error: DIV expects ints\n");
|
fprintf(stderr, "Runtime type error: DIV expects ints, got %s and %s\n",
|
||||||
|
value_type_name(a.type), value_type_name(b.type));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (b.i == 0) {
|
if (b.i == 0) {
|
||||||
|
|
@ -204,7 +218,8 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
Value b = pop_value(vm);
|
Value b = pop_value(vm);
|
||||||
Value a = pop_value(vm);
|
Value a = pop_value(vm);
|
||||||
if (a.type != VAL_INT || b.type != VAL_INT) {
|
if (a.type != VAL_INT || b.type != VAL_INT) {
|
||||||
fprintf(stderr, "Runtime type error: LT expects ints\n");
|
fprintf(stderr, "Runtime type error: LT expects ints, got %s and %s\n",
|
||||||
|
value_type_name(a.type), value_type_name(b.type));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Value res = make_int(a.i < b.i ? 1 : 0);
|
Value res = make_int(a.i < b.i ? 1 : 0);
|
||||||
|
|
@ -441,7 +456,8 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
Value b = pop_value(vm);
|
Value b = pop_value(vm);
|
||||||
Value a = pop_value(vm);
|
Value a = pop_value(vm);
|
||||||
if (a.type != VAL_INT || b.type != VAL_INT) {
|
if (a.type != VAL_INT || b.type != VAL_INT) {
|
||||||
fprintf(stderr, "Runtime type error: MOD expects ints\n");
|
fprintf(stderr, "Runtime type error: MOD expects ints, got %s and %s\n",
|
||||||
|
value_type_name(a.type), value_type_name(b.type));
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (b.i == 0) {
|
if (b.i == 0) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue