Added simple arrays.
This commit is contained in:
parent
0b6cab8672
commit
af16311ed5
8 changed files with 370 additions and 6 deletions
47
examples/arrays.fun
Normal file
47
examples/arrays.fun
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Arrays basics
|
||||||
|
arr = [1, 2, 3]
|
||||||
|
print(arr) // -> [1, 2, 3]
|
||||||
|
print(arr[0] + arr[1]) // -> 3
|
||||||
|
|
||||||
|
// mutate last element
|
||||||
|
arr[2] = arr[2] + 10
|
||||||
|
print(arr) // -> [1, 2, 13]
|
||||||
|
|
||||||
|
// Sum elements with while (fixed length)
|
||||||
|
number i = 0
|
||||||
|
number sum = 0
|
||||||
|
number len = 3
|
||||||
|
while i < len
|
||||||
|
sum = sum + arr[i]
|
||||||
|
i = i + 1
|
||||||
|
print(sum) // -> 16
|
||||||
|
|
||||||
|
// Fill with squares using for in range
|
||||||
|
arr = [0, 0, 0, 0, 0]
|
||||||
|
for i in range(0, 5)
|
||||||
|
arr[i] = i * i
|
||||||
|
print(arr) // -> [0, 1, 4, 9, 16]
|
||||||
|
|
||||||
|
// Nested arrays (matrix)
|
||||||
|
matrix = [[1, 2], [3, 4], [5, 6]]
|
||||||
|
print(matrix) // -> [[1, 2], [3, 4], [5, 6]]
|
||||||
|
print(matrix[1][0]) // -> 3
|
||||||
|
|
||||||
|
// Function that mutates an array in place (first 3 elements)
|
||||||
|
fun scale3(a, s)
|
||||||
|
number i = 0
|
||||||
|
while i < 3
|
||||||
|
a[i] = a[i] * s
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
arr = [2, 4, 6]
|
||||||
|
scale3(arr, 5)
|
||||||
|
print(arr) // -> [10, 20, 30]
|
||||||
|
|
||||||
|
// Function that returns an array
|
||||||
|
fun pair(x, y)
|
||||||
|
return [x, y]
|
||||||
|
|
||||||
|
p = pair(7, 9)
|
||||||
|
print(p) // -> [7, 9]
|
||||||
|
print(p[1]) // -> 9
|
||||||
|
|
@ -72,6 +72,9 @@ static const char *opcode_name(OpCode op) {
|
||||||
case OP_NOT: return "NOT";
|
case OP_NOT: return "NOT";
|
||||||
case OP_DUP: return "DUP";
|
case OP_DUP: return "DUP";
|
||||||
case OP_SWAP: return "SWAP";
|
case OP_SWAP: return "SWAP";
|
||||||
|
case OP_MAKE_ARRAY: return "MAKE_ARRAY";
|
||||||
|
case OP_INDEX_GET: return "INDEX_GET";
|
||||||
|
case OP_INDEX_SET: return "INDEX_SET";
|
||||||
default: return "???";
|
default: return "???";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,11 @@ typedef enum {
|
||||||
|
|
||||||
OP_DUP, // duplicate top of stack
|
OP_DUP, // duplicate top of stack
|
||||||
OP_SWAP, // swap top two stack values
|
OP_SWAP, // swap top two stack values
|
||||||
|
|
||||||
|
// arrays
|
||||||
|
OP_MAKE_ARRAY, // operand = element count; pops N values, pushes array
|
||||||
|
OP_INDEX_GET, // pops index, array; pushes element copy
|
||||||
|
OP_INDEX_SET // pops value, index, array; sets and pushes nothing
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
||||||
140
src/parser.c
140
src/parser.c
|
|
@ -267,6 +267,18 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
parser_fail(*pos, "Expected ')'");
|
parser_fail(*pos, "Expected ')'");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/* postfix indexing */
|
||||||
|
for (;;) {
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == '[') {
|
||||||
|
(*pos)++;
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected index expression"); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after index"); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,6 +288,56 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
int ci = bytecode_add_constant(bc, make_string(s));
|
int ci = bytecode_add_constant(bc, make_string(s));
|
||||||
free(s);
|
free(s);
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||||
|
/* postfix indexing */
|
||||||
|
for (;;) {
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == '[') {
|
||||||
|
(*pos)++;
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected index expression"); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after index"); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* array literal: [expr, expr, ...] */
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == '[') {
|
||||||
|
(*pos)++; /* '[' */
|
||||||
|
int count = 0;
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] != ']') {
|
||||||
|
for (;;) {
|
||||||
|
if (!emit_expression(bc, src, len, pos)) {
|
||||||
|
parser_fail(*pos, "Expected expression in array literal");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
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 ']' to close array literal");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
bytecode_add_instruction(bc, OP_MAKE_ARRAY, count);
|
||||||
|
/* postfix indexing */
|
||||||
|
for (;;) {
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == '[') {
|
||||||
|
(*pos)++;
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected index expression"); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after index"); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,6 +348,18 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
if (ok) {
|
if (ok) {
|
||||||
int ci = bytecode_add_constant(bc, make_int(ival));
|
int ci = bytecode_add_constant(bc, make_int(ival));
|
||||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||||
|
/* postfix indexing */
|
||||||
|
for (;;) {
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == '[') {
|
||||||
|
(*pos)++;
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected index expression"); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after index"); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
*pos = save;
|
*pos = save;
|
||||||
|
|
@ -338,6 +412,18 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
printf("compile: CALL %s with %d arg(s)\n", name, argc);
|
printf("compile: CALL %s with %d arg(s)\n", name, argc);
|
||||||
#endif
|
#endif
|
||||||
bytecode_add_instruction(bc, OP_CALL, argc);
|
bytecode_add_instruction(bc, OP_CALL, argc);
|
||||||
|
/* postfix indexing */
|
||||||
|
for (;;) {
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == '[') {
|
||||||
|
(*pos)++;
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected index expression"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after index"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -347,6 +433,18 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
||||||
int gi = sym_index(name);
|
int gi = sym_index(name);
|
||||||
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||||
}
|
}
|
||||||
|
/* postfix indexing */
|
||||||
|
for (;;) {
|
||||||
|
skip_spaces(src, len, pos);
|
||||||
|
if (*pos < len && src[*pos] == '[') {
|
||||||
|
(*pos)++;
|
||||||
|
if (!emit_expression(bc, src, len, pos)) { parser_fail(*pos, "Expected index expression"); free(name); return 0; }
|
||||||
|
if (!consume_char(src, len, pos, ']')) { parser_fail(*pos, "Expected ']' after index"); free(name); return 0; }
|
||||||
|
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
free(name);
|
free(name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -798,8 +896,48 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
||||||
/* assignment or simple call */
|
/* assignment or simple call */
|
||||||
int lidx = local_find(name);
|
int lidx = local_find(name);
|
||||||
int gi = (lidx < 0) ? sym_index(name) : -1;
|
int gi = (lidx < 0) ? sym_index(name) : -1;
|
||||||
free(name);
|
|
||||||
skip_spaces(src, len, &local_pos);
|
skip_spaces(src, len, &local_pos);
|
||||||
|
|
||||||
|
/* array element assignment: name[expr] = expr */
|
||||||
|
if (local_pos < len && src[local_pos] == '[') {
|
||||||
|
/* load array variable */
|
||||||
|
if (lidx >= 0) {
|
||||||
|
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lidx);
|
||||||
|
} else {
|
||||||
|
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
|
||||||
|
}
|
||||||
|
local_pos++; /* '[' */
|
||||||
|
if (!emit_expression(bc, src, len, &local_pos)) {
|
||||||
|
parser_fail(local_pos, "Expected index expression after '['");
|
||||||
|
free(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!consume_char(src, len, &local_pos, ']')) {
|
||||||
|
parser_fail(local_pos, "Expected ']' after index");
|
||||||
|
free(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skip_spaces(src, len, &local_pos);
|
||||||
|
if (local_pos >= len || src[local_pos] != '=') {
|
||||||
|
parser_fail(local_pos, "Expected '=' after array index");
|
||||||
|
free(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
local_pos++; /* '=' */
|
||||||
|
if (!emit_expression(bc, src, len, &local_pos)) {
|
||||||
|
parser_fail(local_pos, "Expected expression after '='");
|
||||||
|
free(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* perform set */
|
||||||
|
bytecode_add_instruction(bc, OP_INDEX_SET, 0);
|
||||||
|
free(name);
|
||||||
|
*pos = local_pos;
|
||||||
|
skip_to_eol(src, len, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(name);
|
||||||
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)) {
|
||||||
|
|
|
||||||
95
src/value.c
95
src/value.c
|
|
@ -3,6 +3,12 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef struct Array {
|
||||||
|
int refcount;
|
||||||
|
int count;
|
||||||
|
Value *items; /* owns items; each item owned by array */
|
||||||
|
} Array;
|
||||||
|
|
||||||
Value make_int(int64_t v) {
|
Value make_int(int64_t v) {
|
||||||
Value val;
|
Value val;
|
||||||
val.type = VAL_INT;
|
val.type = VAL_INT;
|
||||||
|
|
@ -31,6 +37,58 @@ Value make_nil(void) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value make_array_from_values(const Value *vals, int count) {
|
||||||
|
if (count < 0) count = 0;
|
||||||
|
Array *arr = (Array*)malloc(sizeof(Array));
|
||||||
|
if (!arr) {
|
||||||
|
Value nil = make_nil();
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
arr->refcount = 1;
|
||||||
|
arr->count = count;
|
||||||
|
if (count > 0) {
|
||||||
|
arr->items = (Value*)malloc(sizeof(Value) * count);
|
||||||
|
if (!arr->items) {
|
||||||
|
free(arr);
|
||||||
|
Value nil = make_nil();
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
arr->items[i] = copy_value(&vals[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arr->items = NULL;
|
||||||
|
}
|
||||||
|
Value v;
|
||||||
|
v.type = VAL_ARRAY;
|
||||||
|
v.arr = (struct Array*)arr;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int array_length(const Value *v) {
|
||||||
|
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||||
|
const Array *a = (const Array*)v->arr;
|
||||||
|
return a->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int array_get_copy(const Value *v, int index, Value *out) {
|
||||||
|
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||||
|
const Array *a = (const Array*)v->arr;
|
||||||
|
if (index < 0 || index >= a->count) return 0;
|
||||||
|
if (out) *out = copy_value(&a->items[index]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int array_set(Value *v, int index, Value newElem) {
|
||||||
|
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||||
|
Array *a = (Array*)v->arr;
|
||||||
|
if (index < 0 || index >= a->count) return 0;
|
||||||
|
/* replace element: free old, take ownership of newElem */
|
||||||
|
free_value(a->items[index]);
|
||||||
|
a->items[index] = newElem;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
Value copy_value(const Value *v) {
|
Value copy_value(const Value *v) {
|
||||||
Value out;
|
Value out;
|
||||||
out.type = v->type;
|
out.type = v->type;
|
||||||
|
|
@ -44,6 +102,12 @@ Value copy_value(const Value *v) {
|
||||||
case VAL_FUNCTION:
|
case VAL_FUNCTION:
|
||||||
out.fn = v->fn; /* shallow copy pointer */
|
out.fn = v->fn; /* shallow copy pointer */
|
||||||
break;
|
break;
|
||||||
|
case VAL_ARRAY: {
|
||||||
|
Array *a = (Array*)v->arr;
|
||||||
|
out.arr = (struct Array*)a;
|
||||||
|
if (a) a->refcount++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAL_NIL:
|
case VAL_NIL:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -54,6 +118,15 @@ Value copy_value(const Value *v) {
|
||||||
void free_value(Value v) {
|
void free_value(Value v) {
|
||||||
if (v.type == VAL_STRING && v.s) {
|
if (v.type == VAL_STRING && v.s) {
|
||||||
free(v.s);
|
free(v.s);
|
||||||
|
} else if (v.type == VAL_ARRAY && v.arr) {
|
||||||
|
Array *a = (Array*)v.arr;
|
||||||
|
if (--a->refcount == 0) {
|
||||||
|
for (int i = 0; i < a->count; ++i) {
|
||||||
|
free_value(a->items[i]);
|
||||||
|
}
|
||||||
|
free(a->items);
|
||||||
|
free(a);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */
|
/* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */
|
||||||
}
|
}
|
||||||
|
|
@ -69,6 +142,18 @@ void print_value(const Value *v) {
|
||||||
case VAL_FUNCTION:
|
case VAL_FUNCTION:
|
||||||
printf("<function@%p>", (void*)v->fn);
|
printf("<function@%p>", (void*)v->fn);
|
||||||
break;
|
break;
|
||||||
|
case VAL_ARRAY: {
|
||||||
|
const Array *a = (const Array*)v->arr;
|
||||||
|
printf("[");
|
||||||
|
if (a) {
|
||||||
|
for (int i = 0; i < a->count; ++i) {
|
||||||
|
if (i > 0) printf(", ");
|
||||||
|
print_value(&a->items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
case VAL_NIL:
|
case VAL_NIL:
|
||||||
default:
|
default:
|
||||||
printf("nil");
|
printf("nil");
|
||||||
|
|
@ -84,6 +169,10 @@ int value_is_truthy(const Value *v) {
|
||||||
return v->s && v->s[0] != '\0';
|
return v->s && v->s[0] != '\0';
|
||||||
case VAL_FUNCTION:
|
case VAL_FUNCTION:
|
||||||
return 1;
|
return 1;
|
||||||
|
case VAL_ARRAY: {
|
||||||
|
const Array *a = (const Array*)v->arr;
|
||||||
|
return a && a->count > 0;
|
||||||
|
}
|
||||||
case VAL_NIL:
|
case VAL_NIL:
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -106,6 +195,12 @@ char *value_to_string_alloc(const Value *v) {
|
||||||
snprintf(buf, sizeof(buf), "<function@%p>", (void*)v->fn);
|
snprintf(buf, sizeof(buf), "<function@%p>", (void*)v->fn);
|
||||||
return strdup(buf);
|
return strdup(buf);
|
||||||
}
|
}
|
||||||
|
case VAL_ARRAY: {
|
||||||
|
int n = array_length(v);
|
||||||
|
if (n < 0) n = 0;
|
||||||
|
snprintf(buf, sizeof(buf), "[array n=%d]", n);
|
||||||
|
return strdup(buf);
|
||||||
|
}
|
||||||
case VAL_NIL:
|
case VAL_NIL:
|
||||||
default:
|
default:
|
||||||
return strdup("nil");
|
return strdup("nil");
|
||||||
|
|
|
||||||
15
src/value.h
15
src/value.h
|
|
@ -4,11 +4,13 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
struct Bytecode; /* forward */
|
struct Bytecode; /* forward */
|
||||||
|
struct Array; /* forward */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VAL_INT,
|
VAL_INT,
|
||||||
VAL_STRING,
|
VAL_STRING,
|
||||||
VAL_FUNCTION,
|
VAL_FUNCTION,
|
||||||
|
VAL_ARRAY,
|
||||||
VAL_NIL
|
VAL_NIL
|
||||||
} ValueType;
|
} ValueType;
|
||||||
|
|
||||||
|
|
@ -18,6 +20,7 @@ typedef struct {
|
||||||
int64_t i;
|
int64_t i;
|
||||||
char *s;
|
char *s;
|
||||||
struct Bytecode *fn;
|
struct Bytecode *fn;
|
||||||
|
struct Array *arr;
|
||||||
};
|
};
|
||||||
} Value;
|
} Value;
|
||||||
|
|
||||||
|
|
@ -27,9 +30,15 @@ Value make_string(const char *s);
|
||||||
Value make_function(struct Bytecode *fn);
|
Value make_function(struct Bytecode *fn);
|
||||||
Value make_nil(void);
|
Value make_nil(void);
|
||||||
|
|
||||||
/* copy (deep for strings), free (free string only) */
|
/* arrays */
|
||||||
Value copy_value(const Value *v);
|
Value make_array_from_values(const Value *vals, int count); /* deep-copies vals */
|
||||||
void free_value(Value v);
|
int array_length(const Value *v); /* returns -1 if not array */
|
||||||
|
int array_get_copy(const Value *v, int index, Value *out); /* returns 0 on error; out = copy_value(item) */
|
||||||
|
int array_set(Value *v, int index, Value newElem); /* returns 0 on error; takes ownership of newElem */
|
||||||
|
|
||||||
|
/* copy/free */
|
||||||
|
Value copy_value(const Value *v); /* deep for strings, RC for arrays, shallow fn */
|
||||||
|
void free_value(Value v); /* frees owned resources */
|
||||||
|
|
||||||
/* utilities */
|
/* utilities */
|
||||||
void print_value(const Value *v);
|
void print_value(const Value *v);
|
||||||
|
|
|
||||||
66
src/vm.c
66
src/vm.c
|
|
@ -485,6 +485,72 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OP_MAKE_ARRAY: {
|
||||||
|
int n = inst.operand;
|
||||||
|
if (n < 0 || vm->sp + 1 < n) {
|
||||||
|
fprintf(stderr, "Runtime error: invalid element count for MAKE_ARRAY\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* pop n values into temp array preserving original order */
|
||||||
|
Value *vals = (Value*)malloc(sizeof(Value) * n);
|
||||||
|
if (!vals) { fprintf(stderr, "Runtime error: OOM in MAKE_ARRAY\n"); exit(1); }
|
||||||
|
for (int i = n - 1; i >= 0; --i) {
|
||||||
|
vals[i] = pop_value(vm); /* take ownership */
|
||||||
|
}
|
||||||
|
/* build array by copying values, then free originals */
|
||||||
|
Value arr = make_array_from_values(vals, n);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
free_value(vals[i]);
|
||||||
|
}
|
||||||
|
free(vals);
|
||||||
|
push_value(vm, arr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_INDEX_GET: {
|
||||||
|
Value idx = pop_value(vm);
|
||||||
|
Value arr = pop_value(vm);
|
||||||
|
if (arr.type != VAL_ARRAY) {
|
||||||
|
fprintf(stderr, "Runtime type error: INDEX_GET expects array\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (idx.type != VAL_INT) {
|
||||||
|
fprintf(stderr, "Runtime type error: INDEX_GET index must be int\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Value elem;
|
||||||
|
if (!array_get_copy(&arr, (int)idx.i, &elem)) {
|
||||||
|
fprintf(stderr, "Runtime error: index out of range\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
free_value(arr);
|
||||||
|
free_value(idx);
|
||||||
|
push_value(vm, elem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OP_INDEX_SET: {
|
||||||
|
Value v = pop_value(vm);
|
||||||
|
Value idx = pop_value(vm);
|
||||||
|
Value arr = pop_value(vm);
|
||||||
|
if (arr.type != VAL_ARRAY) {
|
||||||
|
fprintf(stderr, "Runtime type error: INDEX_SET expects array\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (idx.type != VAL_INT) {
|
||||||
|
fprintf(stderr, "Runtime type error: INDEX_SET index must be int\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (!array_set(&arr, (int)idx.i, v)) {
|
||||||
|
fprintf(stderr, "Runtime error: index out of range\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* arr modified in place; do not free v (ownership moved) */
|
||||||
|
free_value(arr);
|
||||||
|
free_value(idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OP_LOAD_GLOBAL: {
|
case OP_LOAD_GLOBAL: {
|
||||||
int idx = inst.operand;
|
int idx = inst.operand;
|
||||||
if (idx < 0 || idx >= VM_MAX_GLOBALS) {
|
if (idx < 0 || idx >= VM_MAX_GLOBALS) {
|
||||||
|
|
|
||||||
5
src/vm.h
5
src/vm.h
|
|
@ -14,7 +14,8 @@ static const char *opcode_names[] = {
|
||||||
"LOAD_GLOBAL","STORE_GLOBAL","ADD","SUB","MUL","DIV",
|
"LOAD_GLOBAL","STORE_GLOBAL","ADD","SUB","MUL","DIV",
|
||||||
"LT","LTE","GT","GTE","EQ","NEQ","POP","JUMP",
|
"LT","LTE","GT","GTE","EQ","NEQ","POP","JUMP",
|
||||||
"JUMP_IF_FALSE","CALL","RETURN","PRINT","HALT",
|
"JUMP_IF_FALSE","CALL","RETURN","PRINT","HALT",
|
||||||
"MOD","AND","OR","NOT","DUP","SWAP"
|
"MOD","AND","OR","NOT","DUP","SWAP",
|
||||||
|
"MAKE_ARRAY","INDEX_GET","INDEX_SET"
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -55,7 +56,7 @@ void vm_dump_globals(VM *vm);
|
||||||
void vm_run(VM *vm, Bytecode *entry);
|
void vm_run(VM *vm, Bytecode *entry);
|
||||||
|
|
||||||
static inline int opcode_is_valid(int op) {
|
static inline int opcode_is_valid(int op) {
|
||||||
return op >= OP_NOP && op <= OP_SWAP; // all current opcodes
|
return op >= OP_NOP && op <= OP_INDEX_SET; // all current opcodes
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue