1
0
Fork 0
forked from fun/fun

Fixed the code style in *.c files to two spaces indentation and add a linter named funstx to Fun. (0.39.0)

This commit is contained in:
Johannes Findeisen 2026-03-18 20:52:00 +01:00
commit 03b2532474
237 changed files with 17550 additions and 13911 deletions

View file

@ -8,7 +8,7 @@
*/
/**
* @file add.c
* @file add.c
* @brief Implements the OP_ADD opcode for arithmetic and string concatenation in the VM.
*
* This file handles the OP_ADD instruction, which performs addition or concatenation
@ -36,50 +36,50 @@
*/
case OP_ADD: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da + db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i + b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
} else if (a.type == VAL_STRING && b.type == VAL_STRING) {
const char *sa = a.s ? a.s : "";
const char *sb = b.s ? b.s : "";
size_t la = strlen(sa);
size_t lb = strlen(sb);
char *buf = (char*)malloc(la + lb + 1);
if (!buf) {
fprintf(stderr, "Runtime error: out of memory during string concatenation\n");
exit(1);
}
memcpy(buf, sa, la);
memcpy(buf + la, sb, lb);
buf[la + lb] = '\0';
Value res;
res.type = VAL_STRING;
res.s = buf;
free_value(a);
free_value(b);
push_value(vm, res);
} else if (a.type == VAL_ARRAY && b.type == VAL_ARRAY) {
Value res = array_concat(&a, &b);
free_value(a);
free_value(b);
push_value(vm, res);
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da + db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: ADD expects both numbers, both strings, or both arrays, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
Value res = make_int(a.i + b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else if (a.type == VAL_STRING && b.type == VAL_STRING) {
const char *sa = a.s ? a.s : "";
const char *sb = b.s ? b.s : "";
size_t la = strlen(sa);
size_t lb = strlen(sb);
char *buf = (char *)malloc(la + lb + 1);
if (!buf) {
fprintf(stderr, "Runtime error: out of memory during string concatenation\n");
exit(1);
}
memcpy(buf, sa, la);
memcpy(buf + la, sb, lb);
buf[la + lb] = '\0';
Value res;
res.type = VAL_STRING;
res.s = buf;
free_value(a);
free_value(b);
push_value(vm, res);
} else if (a.type == VAL_ARRAY && b.type == VAL_ARRAY) {
Value res = array_concat(&a, &b);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: ADD expects both numbers, both strings, or both arrays, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file div.c
* @file div.c
* @brief Implements the OP_DIV opcode for integer division in the VM.
*
* This file handles the OP_DIV instruction, which performs integer division
@ -33,34 +33,34 @@
*/
case OP_DIV: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
if (db == 0.0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_float(da / db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
if (b.i == 0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_int(a.i / b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
if (db == 0.0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_float(da / db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: DIV expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
if (b.i == 0) {
vm_raise_error(vm, "division by zero");
break;
}
Value res = make_int(a.i / b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else {
fprintf(stderr, "Runtime type error: DIV expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file mul.c
* @file mul.c
* @brief Implements the OP_MUL opcode for integer multiplication in the VM.
*
* This file handles the OP_MUL instruction, which performs integer multiplication
@ -32,26 +32,26 @@
*/
case OP_MUL: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da * db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i * b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da * db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: MUL expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
Value res = make_int(a.i * b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else {
fprintf(stderr, "Runtime type error: MUL expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file sub.c
* @file sub.c
* @brief Implements the OP_SUB opcode for integer subtraction in the VM.
*
* This file handles the OP_SUB instruction, which performs integer subtraction
@ -32,26 +32,26 @@
*/
case OP_SUB: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da - db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
Value res = make_int(a.i - b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
Value b = pop_value(vm);
Value a = pop_value(vm);
if ((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT)) {
if (a.type == VAL_FLOAT || b.type == VAL_FLOAT) {
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
Value res = make_float(da - db);
free_value(a);
free_value(b);
push_value(vm, res);
} else {
fprintf(stderr, "Runtime type error: SUB expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
Value res = make_int(a.i - b.i);
free_value(a);
free_value(b);
push_value(vm, res);
}
break;
} else {
fprintf(stderr, "Runtime type error: SUB expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file apop.c
* @file apop.c
* @brief Implements the OP_APOP opcode for removing elements from arrays in the VM.
*
* This file handles the OP_APOP instruction, which removes the last element from an array
@ -33,17 +33,17 @@
*/
case OP_APOP: {
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_APOP expects array\n");
exit(1);
}
Value out;
if (!array_pop(&arr, &out)) {
fprintf(stderr, "Runtime error: pop from empty array\n");
exit(1);
}
free_value(arr);
push_value(vm, out);
break;
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_APOP expects array\n");
exit(1);
}
Value out;
if (!array_pop(&arr, &out)) {
fprintf(stderr, "Runtime error: pop from empty array\n");
exit(1);
}
free_value(arr);
push_value(vm, out);
break;
}

View file

@ -32,13 +32,13 @@
*/
case OP_CLEAR: {
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CLEAR expects array\n");
exit(1);
}
array_clear(&arr);
free_value(arr);
push_value(vm, make_int(0));
break;
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CLEAR expects array\n");
exit(1);
}
array_clear(&arr);
free_value(arr);
push_value(vm, make_int(0));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file contains.c
* @file contains.c
* @brief Implements the OP_CONTAINS opcode for checking array membership in the VM.
*
* This file handles the OP_CONTAINS instruction, which checks if a value is present in an array.
@ -32,15 +32,15 @@
*/
case OP_CONTAINS: {
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CONTAINS expects (array, value)\n");
exit(1);
}
int ok = array_contains(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(ok ? 1 : 0));
break;
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: CONTAINS expects (array, value)\n");
exit(1);
}
int ok = array_contains(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(ok ? 1 : 0));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file enumerate.c
* @file enumerate.c
* @brief Implements the OP_ENUMERATE opcode for enumerating arrays in the VM.
*
* This file handles the OP_ENUMERATE instruction, which creates an array of [index, value] pairs
@ -32,13 +32,13 @@
*/
case OP_ENUMERATE: {
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ENUMERATE expects array\n");
exit(1);
}
Value out = bi_enumerate(&arr);
free_value(arr);
push_value(vm, out);
break;
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ENUMERATE expects array\n");
exit(1);
}
Value out = bi_enumerate(&arr);
free_value(arr);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file index_get.c
* @file index_get.c
* @brief Implements the OP_INDEX_GET opcode for array and map indexing in the VM.
*
* This file handles the OP_INDEX_GET instruction, which retrieves an element from
@ -34,34 +34,41 @@
*/
case OP_INDEX_GET: {
Value idx = pop_value(vm);
Value container = pop_value(vm);
Value idx = pop_value(vm);
Value container = pop_value(vm);
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG INDEX_GET: container.type=%d idx.type=%d\n",
container.type, idx.type);
fprintf(stderr, "DEBUG INDEX_GET: container.type=%d idx.type=%d\n",
container.type, idx.type);
#endif
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_GET index must be int for array\n"); exit(1); }
Value elem;
if (!array_get_copy(&container, (int)idx.i, &elem)) {
fprintf(stderr, "Runtime error: index out of range\n"); exit(1);
}
free_value(container);
free_value(idx);
push_value(vm, elem);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) { fprintf(stderr, "INDEX_GET key must be string for map\n"); exit(1); }
Value out;
if (!map_get_copy(&container, idx.s ? idx.s : "", &out)) {
out = make_nil();
}
free_value(container);
free_value(idx);
push_value(vm, out);
} else {
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map (got container=%s, index=%s)\n",
value_type_name(container.type), value_type_name(idx.type));
exit(1);
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) {
fprintf(stderr, "INDEX_GET index must be int for array\n");
exit(1);
}
break;
Value elem;
if (!array_get_copy(&container, (int)idx.i, &elem)) {
fprintf(stderr, "Runtime error: index out of range\n");
exit(1);
}
free_value(container);
free_value(idx);
push_value(vm, elem);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) {
fprintf(stderr, "INDEX_GET key must be string for map\n");
exit(1);
}
Value out;
if (!map_get_copy(&container, idx.s ? idx.s : "", &out)) {
out = make_nil();
}
free_value(container);
free_value(idx);
push_value(vm, out);
} else {
fprintf(stderr, "Runtime type error: INDEX_GET expects array or map (got container=%s, index=%s)\n",
value_type_name(container.type), value_type_name(idx.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file index_of.c
* @file index_of.c
* @brief Implements the OP_INDEX_OF opcode for finding the index of a value in an array in the VM.
*
* This file handles the OP_INDEX_OF instruction, which finds the index of a value in an array.
@ -32,15 +32,15 @@
*/
case OP_INDEX_OF: {
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: INDEX_OF expects (array, value)\n");
exit(1);
}
int idx = array_index_of(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(idx));
break;
Value needle = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: INDEX_OF expects (array, value)\n");
exit(1);
}
int idx = array_index_of(&arr, &needle);
free_value(arr);
free_value(needle);
push_value(vm, make_int(idx));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file index_set.c
* @file index_set.c
* @brief Implements the OP_INDEX_SET opcode for array and map assignment in the VM.
*
* This file handles the OP_INDEX_SET instruction, which assigns a value to an
@ -32,32 +32,39 @@
* @date 2025-10-16
*/
case OP_INDEX_SET: {
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value container = pop_value(vm);
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value container = pop_value(vm);
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG INDEX_SET: container.type=%d idx.type=%d value.type=%d\n",
container.type, idx.type, v.type);
fprintf(stderr, "DEBUG INDEX_SET: container.type=%d idx.type=%d value.type=%d\n",
container.type, idx.type, v.type);
#endif
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_SET index must be int for array\n"); exit(1); }
if (!array_set(&container, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: index out of range\n"); exit(1);
}
free_value(container);
free_value(idx);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) { fprintf(stderr, "INDEX_SET key must be string for map\n"); exit(1); }
if (!map_set(&container, idx.s ? idx.s : "", v)) {
fprintf(stderr, "Runtime error: map set failed\n"); exit(1);
}
free_value(container);
free_value(idx);
} else {
fprintf(stderr, "Runtime type error: INDEX_SET expects array or map\n");
exit(1);
if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) {
fprintf(stderr, "INDEX_SET index must be int for array\n");
exit(1);
}
break;
if (!array_set(&container, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: index out of range\n");
exit(1);
}
free_value(container);
free_value(idx);
} else if (container.type == VAL_MAP) {
if (idx.type != VAL_STRING) {
fprintf(stderr, "INDEX_SET key must be string for map\n");
exit(1);
}
if (!map_set(&container, idx.s ? idx.s : "", v)) {
fprintf(stderr, "Runtime error: map set failed\n");
exit(1);
}
free_value(container);
free_value(idx);
} else {
fprintf(stderr, "Runtime type error: INDEX_SET expects array or map\n");
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file insert.c
* @file insert.c
* @brief Implements the OP_INSERT opcode for inserting elements into arrays in the VM.
*
* This file handles the OP_INSERT instruction, which inserts a value into an array
@ -34,20 +34,20 @@
*/
case OP_INSERT: {
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_INSERT expects (array, int, value)\n");
exit(1);
}
int n = array_insert(&arr, (int)idx.i, v);
if (n < 0) {
fprintf(stderr, "Runtime error: insert failed (OOM?)\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, make_int(n));
break;
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_INSERT expects (array, int, value)\n");
exit(1);
}
int n = array_insert(&arr, (int)idx.i, v);
if (n < 0) {
fprintf(stderr, "Runtime error: insert failed (OOM?)\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, make_int(n));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file join.c
* @file join.c
* @brief Implements the OP_JOIN opcode for joining array elements into a string in the VM.
*
* This file handles the OP_JOIN instruction, which joins the elements of an array into a string
@ -27,21 +27,21 @@
* // Bytecode: OP_JOIN
* // Stack before: [", ", ["a", "b", "c"]]
* // Stack after: ["a, b, c"]
*
*
* @author Johannes Findeisen
* @date 2025-10-16
*/
case OP_JOIN: {
Value sep = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || sep.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: JOIN expects (array, string)\n");
exit(1);
}
Value out = bi_join(&arr, &sep);
free_value(arr);
free_value(sep);
push_value(vm, out);
break;
Value sep = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || sep.type != VAL_STRING) {
fprintf(stderr, "Runtime type error: JOIN expects (array, string)\n");
exit(1);
}
Value out = bi_join(&arr, &sep);
free_value(arr);
free_value(sep);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file make_array.c
* @file make_array.c
* @brief Implements the OP_MAKE_ARRAY opcode for creating arrays in the VM.
*
* This file handles the OP_MAKE_ARRAY instruction, which pops `n` values from the stack,
@ -33,23 +33,26 @@
*/
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;
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;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file arr_push.c
* @file arr_push.c
* @brief Implements the OP_ARR_PUSH opcode for appending elements to arrays in the VM.
*
* This file handles the OP_ARR_PUSH instruction, which appends a value to the end of an array.
@ -33,18 +33,18 @@
*/
case OP_PUSH: {
Value v = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_PUSH expects array\n");
exit(1);
}
int n = array_push(&arr, v);
if (n < 0) {
fprintf(stderr, "Runtime error: push failed (OOM?)\n");
exit(1);
}
free_value(arr);
push_value(vm, make_int(n));
break;
Value v = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ARR_PUSH expects array\n");
exit(1);
}
int n = array_push(&arr, v);
if (n < 0) {
fprintf(stderr, "Runtime error: push failed (OOM?)\n");
exit(1);
}
free_value(arr);
push_value(vm, make_int(n));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file arr_remove.c
* @file arr_remove.c
* @brief Implements the OP_ARR_REMOVE opcode for removing elements from arrays in the VM.
*
* This file handles the OP_ARR_REMOVE instruction, which removes an element from an array
@ -34,19 +34,19 @@
*/
case OP_REMOVE: {
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_REMOVE expects (array, int)\n");
exit(1);
}
Value out;
if (!array_remove(&arr, (int)idx.i, &out)) {
fprintf(stderr, "Runtime error: remove index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, out);
break;
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_REMOVE expects (array, int)\n");
exit(1);
}
Value out;
if (!array_remove(&arr, (int)idx.i, &out)) {
fprintf(stderr, "Runtime error: remove index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file arr_set.c
* @file arr_set.c
* @brief Implements the OP_ARR_SET opcode for setting elements in arrays in the VM.
*
* This file handles the OP_ARR_SET instruction, which sets a value at a specified index in an array.
@ -33,21 +33,21 @@
*/
case OP_SET: {
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_SET expects (array, int, value)\n");
exit(1);
}
if (!array_set(&arr, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: set index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
/* v already owned by array; push copy for return value */
push_value(vm, copy_value(&v));
free_value(v);
break;
Value v = pop_value(vm);
Value idx = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || idx.type != VAL_INT) {
fprintf(stderr, "Runtime type error: ARR_SET expects (array, int, value)\n");
exit(1);
}
if (!array_set(&arr, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: set index out of range\n");
exit(1);
}
free_value(arr);
free_value(idx);
/* v already owned by array; push copy for return value */
push_value(vm, copy_value(&v));
free_value(v);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file slice.c
* @file slice.c
* @brief Implements the OP_SLICE opcode for array slicing in the VM.
*
* This file handles the OP_SLICE instruction, which creates a new array containing
@ -33,17 +33,17 @@
*/
case OP_SLICE: {
Value end = pop_value(vm);
Value start = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || start.type != VAL_INT || end.type != VAL_INT) {
fprintf(stderr, "Runtime type error: SLICE expects (array, int, int)\n");
exit(1);
}
Value out = array_slice(&arr, (int)start.i, (int)end.i);
free_value(arr);
free_value(start);
free_value(end);
push_value(vm, out);
break;
Value end = pop_value(vm);
Value start = pop_value(vm);
Value arr = pop_value(vm);
if (arr.type != VAL_ARRAY || start.type != VAL_INT || end.type != VAL_INT) {
fprintf(stderr, "Runtime type error: SLICE expects (array, int, int)\n");
exit(1);
}
Value out = array_slice(&arr, (int)start.i, (int)end.i);
free_value(arr);
free_value(start);
free_value(end);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file zip.c
* @file zip.c
* @brief Implements the OP_ZIP opcode for array zipping in the VM.
*
* This file handles the OP_ZIP instruction, which combines two arrays into
@ -24,7 +24,7 @@
* - Exits with error if arguments aren't arrays
*
* Example:
* // Bytecode: OP_ZIP
* // Bytecode: OP_ZIP
* // Stack before: [[1,2], ['a','b']]
* // Stack after: [[[1,'a'], [2,'b']]]
*
@ -33,15 +33,15 @@
*/
case OP_ZIP: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_ARRAY || b.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ZIP expects (array, array)\n");
exit(1);
}
Value out = bi_zip(&a, &b);
free_value(a);
free_value(b);
push_value(vm, out);
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_ARRAY || b.type != VAL_ARRAY) {
fprintf(stderr, "Runtime type error: ZIP expects (array, array)\n");
exit(1);
}
Value out = bi_zip(&a, &b);
free_value(a);
free_value(b);
push_value(vm, out);
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,13 +15,13 @@
* pushes: (uint32_t)(a & b)
*/
case OP_BAND: {
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a & b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a & b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,10 +15,10 @@
* pushes: (uint32_t)(~a)
*/
case OP_BNOT: {
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t r = ~a;
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t r = ~a;
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,13 +15,13 @@
* pushes: (uint32_t)(a | b)
*/
case OP_BOR: {
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a | b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a | b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,13 +15,13 @@
* pushes: (uint32_t)(a ^ b)
*/
case OP_BXOR: {
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a ^ b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vb = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t b = (vb.type == VAL_INT) ? (uint32_t)vb.i : 0u;
uint32_t r = a ^ b;
free_value(vb);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: rotl32(a, s)
*/
case OP_ROTL: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a << s) | (a >> (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a << s) | (a >> (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: rotr32(a, s)
*/
case OP_ROTR: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a >> s) | (a << (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : ((a >> s) | (a << (32u - s)));
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: (uint32_t)(a << (s&31))
*/
case OP_SHL: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a << s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a << s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,14 +15,14 @@
* pushes: (uint32_t)(a >> (s&31)) using logical shift
*/
case OP_SHR: {
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a >> s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
Value vs = pop_value(vm);
Value va = pop_value(vm);
uint32_t a = (va.type == VAL_INT) ? (uint32_t)va.i : 0u;
uint32_t s = (vs.type == VAL_INT) ? (uint32_t)vs.i : 0u;
s &= 31u;
uint32_t r = (s == 0u) ? a : (a >> s);
free_value(vs);
free_value(va);
push_value(vm, make_int((int64_t)(uint64_t)r));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -17,73 +17,77 @@
*/
case OP_CAST: {
/* pop type then value (args pushed in this order: value, typeName) */
Value t = pop_value(vm);
Value v = pop_value(vm);
/* pop type then value (args pushed in this order: value, typeName) */
Value t = pop_value(vm);
Value v = pop_value(vm);
const char *tn = (t.type == VAL_STRING && t.s) ? t.s : NULL;
Value out = make_nil();
const char *tn = (t.type == VAL_STRING && t.s) ? t.s : NULL;
Value out = make_nil();
/* Normalize target name to lowercase into a small buffer */
char target[32];
int k = 0;
if (tn) {
const char *p = tn;
while (*p && k < (int)sizeof(target) - 1) {
char c = *p++;
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
target[k++] = c;
}
/* Normalize target name to lowercase into a small buffer */
char target[32];
int k = 0;
if (tn) {
const char *p = tn;
while (*p && k < (int)sizeof(target) - 1) {
char c = *p++;
if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
target[k++] = c;
}
target[k] = '\0';
}
target[k] = '\0';
if (!tn) {
out = make_nil();
} else if (strcmp(target, "number") == 0) {
if (v.type == VAL_INT) {
out = make_int(v.i);
} else if (v.type == VAL_STRING) {
const char *s = v.s ? v.s : "";
const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
char *endp = NULL;
long long parsed = strtoll(p, &endp, 10);
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n')) endp++;
if (endp && *endp != '\0') out = make_int(0);
else out = make_int((int64_t)parsed);
} else {
out = make_int(0);
}
} else if (strcmp(target, "string") == 0) {
char *s = value_to_string_alloc(&v);
out = make_string(s ? s : "");
if (s) free(s);
} else if (strcmp(target, "array") == 0) {
if (v.type == VAL_ARRAY) {
out = copy_value(&v);
} else {
Value tmp = deep_copy_value(&v);
out = make_array_from_values(&tmp, 1);
free_value(tmp);
}
} else if (strcmp(target, "map") == 0) {
if (v.type == VAL_MAP) {
out = copy_value(&v);
} else {
out = make_map_empty();
}
} else if (strcmp(target, "nil") == 0) {
out = make_nil();
} else if (strcmp(target, "function") == 0) {
out = (v.type == VAL_FUNCTION) ? copy_value(&v) : make_nil();
} else if (strcmp(target, "boolean") == 0) {
out = make_int(value_is_truthy(&v) ? 1 : 0);
if (!tn) {
out = make_nil();
} else if (strcmp(target, "number") == 0) {
if (v.type == VAL_INT) {
out = make_int(v.i);
} else if (v.type == VAL_STRING) {
const char *s = v.s ? v.s : "";
const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
p++;
char *endp = NULL;
long long parsed = strtoll(p, &endp, 10);
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n'))
endp++;
if (endp && *endp != '\0')
out = make_int(0);
else
out = make_int((int64_t)parsed);
} else {
out = make_nil();
out = make_int(0);
}
} else if (strcmp(target, "string") == 0) {
char *s = value_to_string_alloc(&v);
out = make_string(s ? s : "");
if (s) free(s);
} else if (strcmp(target, "array") == 0) {
if (v.type == VAL_ARRAY) {
out = copy_value(&v);
} else {
Value tmp = deep_copy_value(&v);
out = make_array_from_values(&tmp, 1);
free_value(tmp);
}
} else if (strcmp(target, "map") == 0) {
if (v.type == VAL_MAP) {
out = copy_value(&v);
} else {
out = make_map_empty();
}
} else if (strcmp(target, "nil") == 0) {
out = make_nil();
} else if (strcmp(target, "function") == 0) {
out = (v.type == VAL_FUNCTION) ? copy_value(&v) : make_nil();
} else if (strcmp(target, "boolean") == 0) {
out = make_int(value_is_truthy(&v) ? 1 : 0);
} else {
out = make_nil();
}
free_value(t);
free_value(v);
push_value(vm, out);
break;
free_value(t);
free_value(v);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file call.c
* @file call.c
* @brief Implements the OP_CALL opcode for function calls in the VM.
*
* This file handles the OP_CALL instruction, which calls a function with arguments.
@ -28,27 +28,27 @@
*/
case OP_CALL: {
int argc = inst.operand;
if (argc < 0) argc = 0;
/* collect args in reverse (preserve order) */
Value *args = NULL;
if (argc > 0) {
args = (Value*)malloc(sizeof(Value) * argc);
/* pop args into array in reverse */
for (int i = argc - 1; i >= 0; --i) {
args[i] = pop_value(vm);
}
int argc = inst.operand;
if (argc < 0) argc = 0;
/* collect args in reverse (preserve order) */
Value *args = NULL;
if (argc > 0) {
args = (Value *)malloc(sizeof(Value) * argc);
/* pop args into array in reverse */
for (int i = argc - 1; i >= 0; --i) {
args[i] = pop_value(vm);
}
/* pop function value */
Value fnv = pop_value(vm);
if (fnv.type != VAL_FUNCTION) {
fprintf(stderr, "Runtime type error: CALL expects function\n");
exit(1);
}
/* push new frame and transfer args */
vm_push_frame(vm, fnv.fn, argc, args);
/* free args array (locals moved), free fnv (no-op for function) */
free(args);
/* note: fnv contains a pointer to the Bytecode, don't free here */
break;
}
/* pop function value */
Value fnv = pop_value(vm);
if (fnv.type != VAL_FUNCTION) {
fprintf(stderr, "Runtime type error: CALL expects function\n");
exit(1);
}
/* push new frame and transfer args */
vm_push_frame(vm, fnv.fn, argc, args);
/* free args array (locals moved), free fnv (no-op for function) */
free(args);
/* note: fnv contains a pointer to the Bytecode, don't free here */
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file dup.c
* @file dup.c
* @brief Implements the OP_DUP opcode for duplicating the top stack value in the VM.
*
* This file handles the OP_DUP instruction, which duplicates the top value on the stack.
@ -25,17 +25,17 @@
* // Bytecode: OP_DUP
* // Stack before: [42]
* // Stack after: [42, 42]
*
*
* @author Johannes Findeisen
* @date 2025-10-16
*/
case OP_DUP: {
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for DUP\n");
exit(1);
}
Value top = vm->stack[vm->sp];
push_value(vm, copy_value(&top));
break;
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for DUP\n");
exit(1);
}
Value top = vm->stack[vm->sp];
push_value(vm, copy_value(&top));
break;
}

View file

@ -10,7 +10,7 @@
*/
/**
* @file exit.c
* @file exit.c
* @brief Implements the OP_EXIT opcode to terminate the script with an exit code.
*
* Behavior:
@ -20,22 +20,22 @@
*/
case OP_EXIT: {
int code = 0;
if (vm->sp >= 0) {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
code = (int)v.i;
} else if (v.type == VAL_STRING) {
/* best-effort parse number from string */
code = (int)strtoll(v.s, NULL, 10);
} else if (v.type == VAL_NIL) {
code = 0;
} else {
/* unsupported type for exit; default to 0 */
code = 0;
}
free_value(v);
int code = 0;
if (vm->sp >= 0) {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
code = (int)v.i;
} else if (v.type == VAL_STRING) {
/* best-effort parse number from string */
code = (int)strtoll(v.s, NULL, 10);
} else if (v.type == VAL_NIL) {
code = 0;
} else {
/* unsupported type for exit; default to 0 */
code = 0;
}
vm->exit_code = code;
return;
free_value(v);
}
vm->exit_code = code;
return;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file halt.c
* @file halt.c
* @brief Implements the OP_HALT opcode for stopping VM execution.
*
* This file handles the OP_HALT instruction, which stops the execution of the VM.
@ -27,4 +27,4 @@
*/
case OP_HALT:
return;
return;

View file

@ -8,7 +8,7 @@
*/
/**
* @file jump.c
* @file jump.c
* @brief Implements the OP_JUMP opcode for unconditional jumps in the VM.
*
* This file handles the OP_JUMP instruction, which performs an unconditional
@ -28,6 +28,6 @@
*/
case OP_JUMP: {
f->ip = inst.operand;
break;
f->ip = inst.operand;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file jump_if_false.c
* @file jump_if_false.c
* @brief Implements the OP_JUMP_IF_FALSE opcode for conditional jumps in the VM.
*
* This file handles the OP_JUMP_IF_FALSE instruction, which jumps if the top
@ -29,11 +29,11 @@
*/
case OP_JUMP_IF_FALSE: {
Value cond = pop_value(vm);
int truthy = value_is_truthy(&cond);
free_value(cond);
if (!truthy) {
f->ip = inst.operand;
}
break;
Value cond = pop_value(vm);
int truthy = value_is_truthy(&cond);
free_value(cond);
if (!truthy) {
f->ip = inst.operand;
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file load_const.c
* @file load_const.c
* @brief Implements the OP_LOAD_CONST opcode for loading constants in the VM.
*
* This file handles the OP_LOAD_CONST instruction, which loads a constant value
@ -26,12 +26,12 @@
*/
case OP_LOAD_CONST: {
int idx = inst.operand;
if (idx < 0 || idx >= f->fn->const_count) {
fprintf(stderr, "Runtime error: constant index out of range\n");
exit(1);
}
Value c = copy_value(&f->fn->constants[idx]);
push_value(vm, c);
break;
int idx = inst.operand;
if (idx < 0 || idx >= f->fn->const_count) {
fprintf(stderr, "Runtime error: constant index out of range\n");
exit(1);
}
Value c = copy_value(&f->fn->constants[idx]);
push_value(vm, c);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file load_global.c
* @file load_global.c
* @brief Implements the OP_LOAD_GLOBAL opcode for loading global variables in the VM.
*
* This file handles the OP_LOAD_GLOBAL instruction, which loads a global variable
@ -31,14 +31,14 @@
*/
case OP_LOAD_GLOBAL: {
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG LOAD_GLOBAL[%d]: type=%d\n", idx, vm->globals[idx].type);
fprintf(stderr, "DEBUG LOAD_GLOBAL[%d]: type=%d\n", idx, vm->globals[idx].type);
#endif
push_value(vm, copy_value(&vm->globals[idx]));
break;
push_value(vm, copy_value(&vm->globals[idx]));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file load_local.c
* @file load_local.c
* @brief Implements the OP_LOAD_LOCAL opcode for loading local variables in the VM.
*
* This file handles the OP_LOAD_LOCAL instruction, which loads a local variable
@ -31,12 +31,12 @@
*/
case OP_LOAD_LOCAL: {
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value val = copy_value(&f->locals[slot]);
push_value(vm, val);
break;
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value val = copy_value(&f->locals[slot]);
push_value(vm, val);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file nop.c
* @file nop.c
* @brief Implements the OP_NOP opcode for no operation in the VM.
*
* This file handles the OP_NOP instruction, which performs no operation.
@ -27,4 +27,4 @@
*/
case OP_NOP:
break;
break;

View file

@ -8,7 +8,7 @@
*/
/**
* @file pop.c
* @file pop.c
* @brief Implements the OP_POP opcode for removing the top stack value in the VM.
*
* This file handles the OP_POP instruction, which removes the top value from the stack.
@ -29,11 +29,11 @@
*/
case OP_POP: {
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for POP\n");
exit(1);
}
Value v = pop_value(vm);
free_value(v);
break;
if (vm->sp < 0) {
fprintf(stderr, "Runtime error: stack underflow for POP\n");
exit(1);
}
Value v = pop_value(vm);
free_value(v);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file return.c
* @file return.c
* @brief Implements the OP_RETURN opcode for returning from a function in the VM.
*
* This file handles the OP_RETURN instruction, which returns from the current function
@ -32,10 +32,12 @@
*/
case OP_RETURN: {
Value retv;
if (vm->sp >= 0) retv = pop_value(vm);
else retv = make_nil();
vm_pop_frame(vm);
push_value(vm, retv);
break;
Value retv;
if (vm->sp >= 0)
retv = pop_value(vm);
else
retv = make_nil();
vm_pop_frame(vm);
push_value(vm, retv);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file store_global.c
* @file store_global.c
* @brief Implements the OP_STORE_GLOBAL opcode for storing global variables in the VM.
*
* This file handles the OP_STORE_GLOBAL instruction, which stores a value into a global variable
@ -31,16 +31,16 @@
*/
case OP_STORE_GLOBAL: {
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
Value v = pop_value(vm);
int idx = inst.operand;
if (idx < 0 || idx >= MAX_GLOBALS) {
fprintf(stderr, "Runtime error: global index out of range\n");
exit(1);
}
Value v = pop_value(vm);
#ifdef FUN_DEBUG
fprintf(stderr, "DEBUG STORE_GLOBAL[%d]: new.type=%d\n", idx, v.type);
fprintf(stderr, "DEBUG STORE_GLOBAL[%d]: new.type=%d\n", idx, v.type);
#endif
free_value(vm->globals[idx]);
vm->globals[idx] = v;
break;
free_value(vm->globals[idx]);
vm->globals[idx] = v;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file store_local.c
* @file store_local.c
* @brief Implements the OP_STORE_LOCAL opcode for storing local variables in the VM.
*
* This file handles the OP_STORE_LOCAL instruction, which stores a value into a local variable
@ -31,14 +31,14 @@
*/
case OP_STORE_LOCAL: {
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value v = pop_value(vm);
/* free previous local then move v into it */
free_value(f->locals[slot]);
f->locals[slot] = v;
break;
int slot = inst.operand;
if (slot < 0 || slot >= MAX_FRAME_LOCALS) {
fprintf(stderr, "Runtime error: local slot out of range\n");
exit(1);
}
Value v = pop_value(vm);
/* free previous local then move v into it */
free_value(f->locals[slot]);
f->locals[slot] = v;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file swap.c
* @file swap.c
* @brief Implements the OP_SWAP opcode for stack manipulation in the VM.
*
* This file handles the OP_SWAP instruction, which swaps the top two values
@ -26,13 +26,13 @@
*/
case OP_SWAP: {
if (vm->sp < 1) {
fprintf(stderr, "Runtime error: stack underflow for SWAP\n");
exit(1);
}
Value a = vm->stack[vm->sp];
Value b = vm->stack[vm->sp - 1];
vm->stack[vm->sp] = b;
vm->stack[vm->sp - 1] = a;
break;
if (vm->sp < 1) {
fprintf(stderr, "Runtime error: stack underflow for SWAP\n");
exit(1);
}
Value a = vm->stack[vm->sp];
Value b = vm->stack[vm->sp - 1];
vm->stack[vm->sp] = b;
vm->stack[vm->sp - 1] = a;
break;
}

View file

@ -8,26 +8,26 @@
*/
case OP_THROW: {
Value err = pop_value(vm);
/* if there is a handler in this frame, jump to it and push err for catch */
if (f->try_sp >= 0) {
int try_idx = f->try_stack[f->try_sp--];
int target = f->fn->instructions[try_idx].operand;
/* push error for catch block */
push_value(vm, err); /* transfer ownership to stack */
f->ip = target;
break;
}
/* Unhandled: print error and terminate */
char *s = value_to_string_alloc(&err);
if (s) {
fprintf(stdout, "%s\n", s);
free(s);
} else {
fprintf(stdout, "<error>\n");
}
free_value(err);
/* clear frames to stop execution */
vm->fp = -1;
Value err = pop_value(vm);
/* if there is a handler in this frame, jump to it and push err for catch */
if (f->try_sp >= 0) {
int try_idx = f->try_stack[f->try_sp--];
int target = f->fn->instructions[try_idx].operand;
/* push error for catch block */
push_value(vm, err); /* transfer ownership to stack */
f->ip = target;
break;
}
/* Unhandled: print error and terminate */
char *s = value_to_string_alloc(&err);
if (s) {
fprintf(stdout, "%s\n", s);
free(s);
} else {
fprintf(stdout, "<error>\n");
}
free_value(err);
/* clear frames to stop execution */
vm->fp = -1;
break;
}

View file

@ -8,6 +8,6 @@
*/
case OP_TRY_POP: {
if (f->try_sp >= 0) f->try_sp--;
break;
if (f->try_sp >= 0) f->try_sp--;
break;
}

View file

@ -8,11 +8,11 @@
*/
case OP_TRY_PUSH: {
/* push index of this TRY instruction; handler ip is in its operand (may be patched later) */
if (f->try_sp >= (int)(sizeof(f->try_stack)/sizeof(f->try_stack[0])) - 1) {
fprintf(stderr, "Runtime error: try depth exceeded\n");
exit(1);
}
f->try_stack[++f->try_sp] = f->ip - 1; /* index of TRY_PUSH instruction */
break;
/* push index of this TRY instruction; handler ip is in its operand (may be patched later) */
if (f->try_sp >= (int)(sizeof(f->try_stack) / sizeof(f->try_stack[0])) - 1) {
fprintf(stderr, "Runtime error: try depth exceeded\n");
exit(1);
}
f->try_stack[++f->try_sp] = f->ip - 1; /* index of TRY_PUSH instruction */
break;
}

View file

@ -1,5 +1,5 @@
/*
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2026 Johannes Findeisen <you@hanez.org>
@ -21,8 +21,8 @@ extern "C" {
}
extern "C" int fun_op_cpp_add(VM *vm) {
int64_t a = vm_pop_i64(vm);
int64_t b = vm_pop_i64(vm);
vm_push_i64(vm, a + b);
return 0; // success
int64_t a = vm_pop_i64(vm);
int64_t b = vm_pop_i64(vm);
vm_push_i64(vm, a + b);
return 0; // success
}

View file

@ -3,45 +3,53 @@
*/
case OP_CURL_DOWNLOAD: {
#ifdef FUN_WITH_CURL
Value vpath = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *path = value_to_string_alloc(&vpath);
free_value(vurl);
free_value(vpath);
if (!url || !path) {
if (url) free(url);
if (path) free(path);
push_value(vm, make_int(0));
break;
}
FILE *fp = fopen(path, "wb");
if (!fp) {
free(url); free(path);
push_value(vm, make_int(0));
break;
}
CURL *h = curl_easy_init();
if (!h) {
fclose(fp);
free(url); free(path);
push_value(vm, make_int(0));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_file_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, fp);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
fclose(fp);
free(url); free(path);
if (rc != CURLE_OK) { push_value(vm, make_int(0)); break; }
push_value(vm, make_int(1));
#else
Value a = pop_value(vm); free_value(a);
Value b = pop_value(vm); free_value(b);
Value vpath = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *path = value_to_string_alloc(&vpath);
free_value(vurl);
free_value(vpath);
if (!url || !path) {
if (url) free(url);
if (path) free(path);
push_value(vm, make_int(0));
#endif
break;
}
FILE *fp = fopen(path, "wb");
if (!fp) {
free(url);
free(path);
push_value(vm, make_int(0));
break;
}
CURL *h = curl_easy_init();
if (!h) {
fclose(fp);
free(url);
free(path);
push_value(vm, make_int(0));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_file_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, fp);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
fclose(fp);
free(url);
free(path);
if (rc != CURLE_OK) {
push_value(vm, make_int(0));
break;
}
push_value(vm, make_int(1));
#else
Value a = pop_value(vm);
free_value(a);
Value b = pop_value(vm);
free_value(b);
push_value(vm, make_int(0));
#endif
break;
}

View file

@ -3,31 +3,39 @@
*/
case OP_CURL_GET: {
#ifdef FUN_WITH_CURL
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
free_value(vurl);
if (!url) { push_value(vm, make_string("")); break; }
FunCurlBuf buf = { NULL, 0 };
CURL *h = curl_easy_init();
if (!h) { free(url); push_value(vm, make_string("")); break; }
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
free(url);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value v = pop_value(vm); free_value(v);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
free_value(vurl);
if (!url) {
push_value(vm, make_string(""));
#endif
break;
}
FunCurlBuf buf = {NULL, 0};
CURL *h = curl_easy_init();
if (!h) {
free(url);
push_value(vm, make_string(""));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
free(url);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value v = pop_value(vm);
free_value(v);
push_value(vm, make_string(""));
#endif
break;
}

View file

@ -3,39 +3,50 @@
*/
case OP_CURL_POST: {
#ifdef FUN_WITH_CURL
Value vbody = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *body = value_to_string_alloc(&vbody);
free_value(vurl);
free_value(vbody);
if (!url) { if (body) free(body); push_value(vm, make_string("")); break; }
if (!body) body = strdup("");
FunCurlBuf buf = { NULL, 0 };
CURL *h = curl_easy_init();
if (!h) { free(url); free(body); push_value(vm, make_string("")); break; }
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_POST, 1L);
curl_easy_setopt(h, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
Value vbody = pop_value(vm);
Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl);
char *body = value_to_string_alloc(&vbody);
free_value(vurl);
free_value(vbody);
if (!url) {
if (body) free(body);
push_value(vm, make_string(""));
break;
}
if (!body) body = strdup("");
FunCurlBuf buf = {NULL, 0};
CURL *h = curl_easy_init();
if (!h) {
free(url);
free(body);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value a = pop_value(vm); free_value(a);
Value b = pop_value(vm); free_value(b);
push_value(vm, make_string(""));
#endif
break;
}
curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_POST, 1L);
curl_easy_setopt(h, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
curl_easy_setopt(h, CURLOPT_WRITEDATA, &buf);
CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h);
free(url);
free(body);
if (rc != CURLE_OK) {
if (buf.d) free(buf.d);
push_value(vm, make_string(""));
break;
}
Value s = make_string(buf.d ? buf.d : "");
if (buf.d) free(buf.d);
push_value(vm, s);
#else
Value a = pop_value(vm);
free_value(a);
Value b = pop_value(vm);
free_value(b);
push_value(vm, make_string(""));
#endif
break;
}

View file

@ -1,5 +1,5 @@
/*
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -7,25 +7,25 @@
* https://opensource.org/license/apache-2-0
*/
/**
/**
* Implements OP_ECHO: print top-of-stack value without trailing newline.
* Now stores the value into the VM's output buffer and marks it as partial,
* so the CLI can render echo output together with following print output.
*/
case OP_ECHO: {
Value v = pop_value(vm);
Value snap = deep_copy_value(&v);
free_value(v);
if (vm->output_count < OUTPUT_SIZE) {
int idx = vm->output_count;
vm->output[idx] = snap;
vm->output_is_partial[idx] = 1; // ECHO does not end the line
vm->output_count++;
} else {
free_value(snap);
fprintf(stderr, "Runtime error: output buffer overflow\n");
exit(1);
}
break;
Value v = pop_value(vm);
Value snap = deep_copy_value(&v);
free_value(v);
if (vm->output_count < OUTPUT_SIZE) {
int idx = vm->output_count;
vm->output[idx] = snap;
vm->output_is_partial[idx] = 1; // ECHO does not end the line
vm->output_count++;
} else {
free_value(snap);
fprintf(stderr, "Runtime error: output buffer overflow\n");
exit(1);
}
break;
}

View file

@ -12,11 +12,11 @@
/* OP_INI_FREE: pops handle; pushes 1/0 */
#ifdef FUN_WITH_INI
case OP_INI_FREE: {
Value vh = pop_value(vm);
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
free_value(vh);
int ok = ini_free_handle(h);
push_value(vm, make_int(ok));
break;
Value vh = pop_value(vm);
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
free_value(vh);
int ok = ini_free_handle(h);
push_value(vm, make_int(ok));
break;
}
#endif

View file

@ -12,51 +12,67 @@
/* OP_INI_GET_BOOL */
#ifdef FUN_WITH_INI
case OP_INI_GET_BOOL: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type==VAL_INT||vdef.type==VAL_BOOL) ? (int)vdef.i : 0;
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outb = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* normalize and parse boolean */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
memcpy(buf, s+1, copy); buf[copy] = '\0';
s = buf;
}
/* trim spaces */
while (*s && (unsigned char)*s <= ' ') s++;
/* lower copy for textual booleans */
char lb[256]; size_t li=0; for (; s[li] && li < sizeof(lb)-1; ++li) lb[li] = (char)tolower((unsigned char)s[li]); lb[li]='\0';
if (strcmp(lb, "true")==0 || strcmp(lb, "yes")==0 || strcmp(lb, "on")==0) {
outb = 1;
} else if (strcmp(lb, "false")==0 || strcmp(lb, "no")==0 || strcmp(lb, "off")==0) {
outb = 0;
} else {
/* numeric */
char *endp=NULL; long v = strtol(lb, &endp, 10);
outb = (endp && endp!=lb) ? (v!=0) : def;
}
} else {
outb = def;
}
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type == VAL_INT || vdef.type == VAL_BOOL) ? (int)vdef.i : 0;
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outb = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_int(outb ? 1 : 0));
break;
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* normalize and parse boolean */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
memcpy(buf, s + 1, copy);
buf[copy] = '\0';
s = buf;
}
/* trim spaces */
while (*s && (unsigned char)*s <= ' ')
s++;
/* lower copy for textual booleans */
char lb[256];
size_t li = 0;
for (; s[li] && li < sizeof(lb) - 1; ++li)
lb[li] = (char)tolower((unsigned char)s[li]);
lb[li] = '\0';
if (strcmp(lb, "true") == 0 || strcmp(lb, "yes") == 0 || strcmp(lb, "on") == 0) {
outb = 1;
} else if (strcmp(lb, "false") == 0 || strcmp(lb, "no") == 0 || strcmp(lb, "off") == 0) {
outb = 0;
} else {
/* numeric */
char *endp = NULL;
long v = strtol(lb, &endp, 10);
outb = (endp && endp != lb) ? (v != 0) : def;
}
} else {
outb = def;
}
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_int(outb ? 1 : 0));
break;
}
#endif

View file

@ -12,41 +12,55 @@
/* OP_INI_GET_DOUBLE */
#ifdef FUN_WITH_INI
case OP_INI_GET_DOUBLE: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
double def = (vdef.type==VAL_FLOAT) ? vdef.d : (vdef.type==VAL_INT ? (double)vdef.i : 0.0);
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
double outd = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
memcpy(buf, s+1, copy); buf[copy] = '\0';
s = buf;
}
while (*s && (unsigned char)*s <= ' ') s++;
char *endp = NULL;
double v = strtod(s, &endp);
if (endp && endp != s) outd = v; else outd = def;
} else {
outd = def;
}
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
double def = (vdef.type == VAL_FLOAT) ? vdef.d : (vdef.type == VAL_INT ? (double)vdef.i : 0.0);
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
double outd = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_float(outd));
break;
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
memcpy(buf, s + 1, copy);
buf[copy] = '\0';
s = buf;
}
while (*s && (unsigned char)*s <= ' ')
s++;
char *endp = NULL;
double v = strtod(s, &endp);
if (endp && endp != s)
outd = v;
else
outd = def;
} else {
outd = def;
}
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_float(outd));
break;
}
#endif

View file

@ -12,43 +12,57 @@
/* OP_INI_GET_INT */
#ifdef FUN_WITH_INI
case OP_INI_GET_INT: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type==VAL_INT) ? (int)vdef.i : 0;
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outi = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* strip optional quotes and parse */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
memcpy(buf, s+1, copy); buf[copy] = '\0';
s = buf;
}
/* skip leading spaces */
while (*s && (unsigned char)*s <= ' ') s++;
char *endp = NULL;
long v = strtol(s, &endp, 10);
if (endp && endp != s) outi = (int)v; else outi = def;
} else {
outi = def;
}
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
int def = (vdef.type == VAL_INT) ? (int)vdef.i : 0;
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
int outi = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_int(outi));
break;
const char *s = iniparser_getstring(d, full, NULL);
if (!s) s = iniparser_getstring(d, alt, NULL);
if (s) {
/* strip optional quotes and parse */
char buf[256];
size_t n = strlen(s);
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
memcpy(buf, s + 1, copy);
buf[copy] = '\0';
s = buf;
}
/* skip leading spaces */
while (*s && (unsigned char)*s <= ' ')
s++;
char *endp = NULL;
long v = strtol(s, &endp, 10);
if (endp && endp != s)
outi = (int)v;
else
outi = def;
} else {
outi = def;
}
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_int(outi));
break;
}
#endif

View file

@ -12,34 +12,42 @@
/* OP_INI_GET_STRING */
#ifdef FUN_WITH_INI
case OP_INI_GET_STRING: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
const char *def = (vdef.type==VAL_STRING && vdef.s) ? vdef.s : "";
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
const char *res = def;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
/* Build alternate with dot separator for robustness */
ini_make_full_key(alt, sizeof(alt), sec, key);
size_t flen = strlen(full);
if (flen < sizeof(alt) && flen > 0) { /* create dot version in alt */
memcpy(alt, full, flen + 1);
for (size_t i = 0; i < flen; ++i) if (alt[i] == ':') { alt[i] = '.'; break; }
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
const char *def = (vdef.type == VAL_STRING && vdef.s) ? vdef.s : "";
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
dictionary *d = ini_get(h);
const char *res = def;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
/* Build alternate with dot separator for robustness */
ini_make_full_key(alt, sizeof(alt), sec, key);
size_t flen = strlen(full);
if (flen < sizeof(alt) && flen > 0) { /* create dot version in alt */
memcpy(alt, full, flen + 1);
for (size_t i = 0; i < flen; ++i)
if (alt[i] == ':') {
alt[i] = '.';
break;
}
const char *s = iniparser_getstring(d, full, def);
if (s == def) { /* not found, try alternate dot form */
s = iniparser_getstring(d, alt, def);
}
res = s ? s : "";
}
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_string(res));
break;
const char *s = iniparser_getstring(d, full, def);
if (s == def) { /* not found, try alternate dot form */
s = iniparser_getstring(d, alt, def);
}
res = s ? s : "";
}
free_value(vdef);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_string(res));
break;
}
#endif

View file

@ -9,55 +9,59 @@
#ifdef FUN_WITH_INI
#if defined(__has_include)
# if __has_include(<iniparser/iniparser.h>)
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
# elif __has_include(<iniparser.h>)
# include <iniparser.h>
# include <dictionary.h>
# else
# error "iniparser headers not found"
# endif
#if __has_include(<iniparser/iniparser.h>)
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#elif __has_include(<iniparser.h>)
#include <dictionary.h>
#include <iniparser.h>
#else
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
#error "iniparser headers not found"
#endif
#else
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <string.h>
#include "handles.h"
IniSlot g_ini[64];
int ini_alloc_handle(dictionary *d) {
if (!d) return 0;
for (int i = 1; i < (int)(sizeof(g_ini)/sizeof(g_ini[0])); ++i) {
if (!g_ini[i].in_use) { g_ini[i].in_use = 1; g_ini[i].dict = d; return i; }
if (!d) return 0;
for (int i = 1; i < (int)(sizeof(g_ini) / sizeof(g_ini[0])); ++i) {
if (!g_ini[i].in_use) {
g_ini[i].in_use = 1;
g_ini[i].dict = d;
return i;
}
return 0;
}
return 0;
}
dictionary* ini_get(int h) {
if (h > 0 && h < (int)(sizeof(g_ini)/sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
return NULL;
dictionary *ini_get(int h) {
if (h > 0 && h < (int)(sizeof(g_ini) / sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
return NULL;
}
int ini_free_handle(int h) {
if (h <= 0 || h >= (int)(sizeof(g_ini)/sizeof(g_ini[0])) || !g_ini[h].in_use) return 0;
if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
g_ini[h].dict = NULL;
g_ini[h].in_use = 0;
return 1;
if (h <= 0 || h >= (int)(sizeof(g_ini) / sizeof(g_ini[0])) || !g_ini[h].in_use) return 0;
if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
g_ini[h].dict = NULL;
g_ini[h].in_use = 0;
return 1;
}
void ini_make_full_key(char *buf, size_t cap, const char *sec, const char *key) {
if (!buf || cap == 0) return;
if (!sec) sec = "";
if (!key) key = "";
/* iniparser expects section:key; lookup is case-insensitive internally */
snprintf(buf, cap, "%s:%s", sec, key);
if (!buf || cap == 0) return;
if (!sec) sec = "";
if (!key) key = "";
/* iniparser expects section:key; lookup is case-insensitive internally */
snprintf(buf, cap, "%s:%s", sec, key);
}
#endif /* FUN_WITH_INI */

View file

@ -14,29 +14,32 @@
#ifdef FUN_WITH_INI
#if defined(__has_include)
# if __has_include(<iniparser/iniparser.h>)
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
# elif __has_include(<iniparser.h>)
# include <iniparser.h>
# include <dictionary.h>
# else
# error "iniparser headers not found"
# endif
#if __has_include(<iniparser/iniparser.h>)
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#elif __has_include(<iniparser.h>)
#include <dictionary.h>
#include <iniparser.h>
#else
# include <iniparser/iniparser.h>
# include <iniparser/dictionary.h>
#error "iniparser headers not found"
#endif
#else
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif
#include <stddef.h>
typedef struct { dictionary *dict; int in_use; } IniSlot;
typedef struct {
dictionary *dict;
int in_use;
} IniSlot;
/* Single global registry (defined in handles.c) */
extern IniSlot g_ini[64];
/* Registry API (implemented in handles.c) */
int ini_alloc_handle(dictionary *d);
dictionary* ini_get(int h);
dictionary *ini_get(int h);
int ini_free_handle(int h);
/* Helper to build section:key string safely into provided buffer (implemented in handles.c) */

View file

@ -12,18 +12,20 @@
/* OP_INI_LOAD: pops path string; pushes handle (>0) or 0 */
#ifdef FUN_WITH_INI
case OP_INI_LOAD: {
Value vpath = pop_value(vm);
const char *path = (vpath.type == VAL_STRING && vpath.s) ? vpath.s : NULL;
int h = 0;
if (path) {
dictionary *d = iniparser_load(path);
if (d) {
h = ini_alloc_handle(d);
if (!h) { iniparser_freedict(d); }
}
Value vpath = pop_value(vm);
const char *path = (vpath.type == VAL_STRING && vpath.s) ? vpath.s : NULL;
int h = 0;
if (path) {
dictionary *d = iniparser_load(path);
if (d) {
h = ini_alloc_handle(d);
if (!h) {
iniparser_freedict(d);
}
}
free_value(vpath);
push_value(vm, make_int(h));
break;
}
free_value(vpath);
push_value(vm, make_int(h));
break;
}
#endif

View file

@ -12,17 +12,22 @@
/* OP_INI_SAVE */
#ifdef FUN_WITH_INI
case OP_INI_SAVE: {
Value vpath = pop_value(vm);
Value vh = pop_value(vm);
const char *path = (vpath.type==VAL_STRING)?vpath.s:NULL;
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0);
int ok = 0;
if (d && path) {
FILE *f = fopen(path, "w");
if (f) { iniparser_dump_ini(d, f); fclose(f); ok = 1; }
Value vpath = pop_value(vm);
Value vh = pop_value(vm);
const char *path = (vpath.type == VAL_STRING) ? vpath.s : NULL;
dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
int ok = 0;
if (d && path) {
FILE *f = fopen(path, "w");
if (f) {
iniparser_dump_ini(d, f);
fclose(f);
ok = 1;
}
free_value(vpath); free_value(vh);
push_value(vm, make_int(ok));
break;
}
free_value(vpath);
free_value(vh);
push_value(vm, make_int(ok));
break;
}
#endif

View file

@ -12,32 +12,41 @@
/* OP_INI_SET */
#ifdef FUN_WITH_INI
case OP_INI_SET: {
Value vval = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0);
const char *key = (vkey.type==VAL_STRING)?vkey.s:NULL;
const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL;
int ok = 0;
if (d && sec && key) {
char *valstr = value_to_string_alloc(&vval);
if (valstr) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
/* iniparser 4.x does not expose iniparser_set; use dictionary_set */
if (dictionary_set(d, full, valstr) == 0) {
ok = 1; /* 0 means success */
} else if (dictionary_set(d, alt, valstr) == 0) {
ok = 1;
}
free(valstr);
Value vval = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int ok = 0;
if (d && sec && key) {
char *valstr = value_to_string_alloc(&vval);
if (valstr) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
/* iniparser 4.x does not expose iniparser_set; use dictionary_set */
if (dictionary_set(d, full, valstr) == 0) {
ok = 1; /* 0 means success */
} else if (dictionary_set(d, alt, valstr) == 0) {
ok = 1;
}
free(valstr);
}
free_value(vval); free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_int(ok));
break;
}
free_value(vval);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_int(ok));
break;
}
#endif

View file

@ -5,100 +5,114 @@
/* OP_INI_LOAD: pops path string; pushes 0 (invalid handle) */
case OP_INI_LOAD: {
Value vpath = pop_value(vm);
(void)vpath; /* unused */
free_value(vpath);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
Value vpath = pop_value(vm);
(void)vpath; /* unused */
free_value(vpath);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
}
/* OP_INI_FREE: pops handle; pushes 0 */
case OP_INI_FREE: {
Value vh = pop_value(vm);
free_value(vh);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
Value vh = pop_value(vm);
free_value(vh);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
}
/* Getters: pop args; push defaults (string:"", int:0, double:0.0, bool:0) */
case OP_INI_GET_STRING: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey);
/* cannot convert here; return empty string */
push_value(vm, make_string(""));
free_value(vdef);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh);
free_value(vsec);
free_value(vkey);
/* cannot convert here; return empty string */
push_value(vm, make_string(""));
free_value(vdef);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
}
case OP_INI_GET_INT: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey);
(void)vdef; /* unused */
push_value(vm, make_int(0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh);
free_value(vsec);
free_value(vkey);
(void)vdef; /* unused */
push_value(vm, make_int(0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
}
case OP_INI_GET_DOUBLE: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey);
(void)vdef; /* unused */
push_value(vm, make_float(0.0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh);
free_value(vsec);
free_value(vkey);
(void)vdef; /* unused */
push_value(vm, make_float(0.0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
}
case OP_INI_GET_BOOL: {
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey);
(void)vdef; /* unused */
push_value(vm, make_int(0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
Value vdef = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh);
free_value(vsec);
free_value(vkey);
(void)vdef; /* unused */
push_value(vm, make_int(0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
break;
}
/* Mutators: return 0 */
case OP_INI_SET: {
Value vval = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey); free_value(vval);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
Value vval = pop_value(vm);
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh);
free_value(vsec);
free_value(vkey);
free_value(vval);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
}
case OP_INI_UNSET: {
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh);
free_value(vsec);
free_value(vkey);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
}
case OP_INI_SAVE: {
Value vpath = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh); free_value(vpath);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
Value vpath = pop_value(vm);
Value vh = pop_value(vm);
free_value(vh);
free_value(vpath);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0));
break;
}

View file

@ -12,25 +12,33 @@
/* OP_INI_UNSET */
#ifdef FUN_WITH_INI
case OP_INI_UNSET: {
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0);
const char *key = (vkey.type==VAL_STRING)?vkey.s:NULL;
const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL;
int ok = 0;
if (d && sec && key) {
char full[1024]; char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
/* iniparser 4.2.6 dictionary_unset returns void; remove both forms */
dictionary_unset(d, full);
dictionary_unset(d, alt);
ok = 1;
Value vkey = pop_value(vm);
Value vsec = pop_value(vm);
Value vh = pop_value(vm);
dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int ok = 0;
if (d && sec && key) {
char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
if (alt[i] == ':') {
alt[i] = '.';
break;
}
}
free_value(vkey); free_value(vsec); free_value(vh);
push_value(vm, make_int(ok));
break;
/* iniparser 4.2.6 dictionary_unset returns void; remove both forms */
dictionary_unset(d, full);
dictionary_unset(d, alt);
ok = 1;
}
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_int(ok));
break;
}
#endif

View file

@ -1,137 +1,137 @@
case OP_INPUT_LINE: {
/* operand bit flags:
* bit0 (1): has prompt (string or any value convertible to string) top of stack holds prompt when set
* bit1 (2): hidden input (do not echo typed characters)
*/
int has_prompt = (inst.operand & 1) ? 1 : 0;
int hidden = (inst.operand & 2) ? 1 : 0;
if (has_prompt) {
/* pop prompt value and print without newline */
Value pv = pop_value(vm);
char *pstr = value_to_string_alloc(&pv);
if (pstr) {
fputs(pstr, stdout);
fflush(stdout);
free(pstr);
}
free_value(pv);
/* operand bit flags:
* bit0 (1): has prompt (string or any value convertible to string) top of stack holds prompt when set
* bit1 (2): hidden input (do not echo typed characters)
*/
int has_prompt = (inst.operand & 1) ? 1 : 0;
int hidden = (inst.operand & 2) ? 1 : 0;
if (has_prompt) {
/* pop prompt value and print without newline */
Value pv = pop_value(vm);
char *pstr = value_to_string_alloc(&pv);
if (pstr) {
fputs(pstr, stdout);
fflush(stdout);
free(pstr);
}
free_value(pv);
}
/* For hidden input, temporarily disable terminal echo if possible */
int echo_disabled = 0;
/* For hidden input, temporarily disable terminal echo if possible */
int echo_disabled = 0;
#ifdef _WIN32
if (hidden) {
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (hStdin != INVALID_HANDLE_VALUE) {
DWORD mode;
if (GetConsoleMode(hStdin, &mode)) {
DWORD newMode = mode & ~(ENABLE_ECHO_INPUT);
if (SetConsoleMode(hStdin, newMode)) {
echo_disabled = 1;
}
}
if (hidden) {
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (hStdin != INVALID_HANDLE_VALUE) {
DWORD mode;
if (GetConsoleMode(hStdin, &mode)) {
DWORD newMode = mode & ~(ENABLE_ECHO_INPUT);
if (SetConsoleMode(hStdin, newMode)) {
echo_disabled = 1;
}
}
}
}
#else
if (hidden) {
/* POSIX termios */
struct termios oldt;
if (tcgetattr(STDIN_FILENO, &oldt) == 0) {
struct termios newt = oldt;
newt.c_lflag &= ~(ECHO);
if (tcsetattr(STDIN_FILENO, TCSANOW, &newt) == 0) {
echo_disabled = 1;
}
}
if (hidden) {
/* POSIX termios */
struct termios oldt;
if (tcgetattr(STDIN_FILENO, &oldt) == 0) {
struct termios newt = oldt;
newt.c_lflag &= ~(ECHO);
if (tcsetattr(STDIN_FILENO, TCSANOW, &newt) == 0) {
echo_disabled = 1;
}
}
}
#endif
/* read a line from stdin, dynamically grow buffer */
size_t cap = 128;
size_t len = 0;
char *buf = (char*)malloc(cap);
if (!buf) {
/* read a line from stdin, dynamically grow buffer */
size_t cap = 128;
size_t len = 0;
char *buf = (char *)malloc(cap);
if (!buf) {
fprintf(stderr, "Runtime error: out of memory reading input");
push_value(vm, make_string(""));
/* On early exit, try to restore echo if we turned it off */
goto restore_echo_and_break;
}
int ch;
while ((ch = fgetc(stdin)) != EOF) {
if (ch == '\r') {
/* Handle CRLF by consuming optional following '\n' */
int next = fgetc(stdin);
if (next != EOF && next != '\n') {
ungetc(next, stdin);
}
break;
}
if (ch == '\n') {
break;
}
if (len + 1 >= cap) {
cap *= 2;
char *nb = (char *)realloc(buf, cap);
if (!nb) {
free(buf);
fprintf(stderr, "Runtime error: out of memory reading input");
push_value(vm, make_string(""));
/* On early exit, try to restore echo if we turned it off */
goto restore_echo_and_break;
goto push_done;
}
buf = nb;
}
buf[len++] = (char)ch;
}
int ch;
while ((ch = fgetc(stdin)) != EOF) {
if (ch == '\r') {
/* Handle CRLF by consuming optional following '\n' */
int next = fgetc(stdin);
if (next != EOF && next != '\n') {
ungetc(next, stdin);
}
break;
}
if (ch == '\n') {
break;
}
if (len + 1 >= cap) {
cap *= 2;
char *nb = (char*)realloc(buf, cap);
if (!nb) {
free(buf);
fprintf(stderr, "Runtime error: out of memory reading input");
push_value(vm, make_string(""));
goto push_done;
}
buf = nb;
}
buf[len++] = (char)ch;
/* null-terminate */
if (len + 1 >= cap) {
char *nb = (char *)realloc(buf, len + 1);
if (!nb) {
free(buf);
fprintf(stderr, "Runtime error: out of memory finalizing input");
push_value(vm, make_string(""));
goto push_done;
}
buf = nb;
}
buf[len] = '\0';
/* null-terminate */
if (len + 1 >= cap) {
char *nb = (char*)realloc(buf, len + 1);
if (!nb) {
free(buf);
fprintf(stderr, "Runtime error: out of memory finalizing input");
push_value(vm, make_string(""));
goto push_done;
}
buf = nb;
}
buf[len] = '\0';
/* push as Fun string */
push_value(vm, make_string(buf));
free(buf);
/* push as Fun string */
push_value(vm, make_string(buf));
free(buf);
push_done:
/* If we disabled echo, restore terminal settings and print a newline for UX */
/* If we disabled echo, restore terminal settings and print a newline for UX */
#ifdef _WIN32
if (echo_disabled) {
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (hStdin != INVALID_HANDLE_VALUE) {
DWORD mode;
if (GetConsoleMode(hStdin, &mode)) {
/* Re-enable ECHO flag */
mode |= ENABLE_ECHO_INPUT;
SetConsoleMode(hStdin, mode);
}
}
if (has_prompt) {
fputc('\n', stdout);
fflush(stdout);
}
if (echo_disabled) {
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (hStdin != INVALID_HANDLE_VALUE) {
DWORD mode;
if (GetConsoleMode(hStdin, &mode)) {
/* Re-enable ECHO flag */
mode |= ENABLE_ECHO_INPUT;
SetConsoleMode(hStdin, mode);
}
}
if (has_prompt) {
fputc('\n', stdout);
fflush(stdout);
}
}
#else
if (echo_disabled) {
struct termios t;
if (tcgetattr(STDIN_FILENO, &t) == 0) {
t.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &t);
}
if (has_prompt) {
fputc('\n', stdout);
fflush(stdout);
}
if (echo_disabled) {
struct termios t;
if (tcgetattr(STDIN_FILENO, &t) == 0) {
t.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &t);
}
if (has_prompt) {
fputc('\n', stdout);
fflush(stdout);
}
}
#endif
restore_echo_and_break:
break;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file read_file.c
* @file read_file.c
* @brief Implements the OP_READ_FILE opcode for reading file contents in the VM.
*
* This file handles the OP_READ_FILE instruction, which reads the contents of a file
@ -32,23 +32,44 @@
*/
case OP_READ_FILE: {
Value path = pop_value(vm);
if (path.type != VAL_STRING) { fprintf(stderr, "READ_FILE expects string\n"); exit(1); }
const char *p = path.s ? path.s : "";
FILE *f = fopen(p, "rb");
if (!f) { free_value(path); push_value(vm, make_string("")); break; }
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); free_value(path); push_value(vm, make_string("")); break; }
long sz = ftell(f);
if (sz < 0) { fclose(f); free_value(path); push_value(vm, make_string("")); break; }
rewind(f);
char *buf = (char*)malloc((size_t)sz + 1);
size_t n = buf ? fread(buf, 1, (size_t)sz, f) : 0;
fclose(f);
if (!buf) { free_value(path); push_value(vm, make_string("")); break; }
buf[n] = '\0';
Value out = make_string(buf);
free(buf);
Value path = pop_value(vm);
if (path.type != VAL_STRING) {
fprintf(stderr, "READ_FILE expects string\n");
exit(1);
}
const char *p = path.s ? path.s : "";
FILE *f = fopen(p, "rb");
if (!f) {
free_value(path);
push_value(vm, out);
push_value(vm, make_string(""));
break;
}
if (fseek(f, 0, SEEK_END) != 0) {
fclose(f);
free_value(path);
push_value(vm, make_string(""));
break;
}
long sz = ftell(f);
if (sz < 0) {
fclose(f);
free_value(path);
push_value(vm, make_string(""));
break;
}
rewind(f);
char *buf = (char *)malloc((size_t)sz + 1);
size_t n = buf ? fread(buf, 1, (size_t)sz, f) : 0;
fclose(f);
if (!buf) {
free_value(path);
push_value(vm, make_string(""));
break;
}
buf[n] = '\0';
Value out = make_string(buf);
free(buf);
free_value(path);
push_value(vm, out);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file write_file.c
* @file write_file.c
* @brief Implements the OP_WRITE_FILE opcode for writing to a file in the VM.
*
* This file handles the OP_WRITE_FILE instruction, which writes data to a file.
@ -32,19 +32,22 @@
*/
case OP_WRITE_FILE: {
Value data = pop_value(vm);
Value path = pop_value(vm);
if (path.type != VAL_STRING || data.type != VAL_STRING) { fprintf(stderr, "WRITE_FILE expects (string, string)\n"); exit(1); }
const char *p = path.s ? path.s : "";
FILE *f = fopen(p, "wb");
int ok = 0;
if (f) {
size_t len = data.s ? strlen(data.s) : 0;
ok = (fwrite(data.s ? data.s : "", 1, len, f) == len);
fclose(f);
}
free_value(path);
free_value(data);
push_value(vm, make_int(ok ? 1 : 0));
break;
Value data = pop_value(vm);
Value path = pop_value(vm);
if (path.type != VAL_STRING || data.type != VAL_STRING) {
fprintf(stderr, "WRITE_FILE expects (string, string)\n");
exit(1);
}
const char *p = path.s ? path.s : "";
FILE *f = fopen(p, "wb");
int ok = 0;
if (f) {
size_t len = data.s ? strlen(data.s) : 0;
ok = (fwrite(data.s ? data.s : "", 1, len, f) == len);
fclose(f);
}
free_value(path);
free_value(data);
push_value(vm, make_int(ok ? 1 : 0));
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -12,19 +12,26 @@
/* JSON_FROM_FILE */
case OP_JSON_FROM_FILE: {
#ifdef FUN_WITH_JSON
Value vpath = pop_value(vm);
char *path = value_to_string_alloc(&vpath);
free_value(vpath);
if (!path) { push_value(vm, make_nil()); break; }
json_object *root = json_object_from_file(path);
free(path);
if (!root) { push_value(vm, make_nil()); break; }
Value v = json_to_fun(root);
push_value(vm, v);
json_object_put(root);
#else
Value vpath = pop_value(vm); free_value(vpath);
Value vpath = pop_value(vm);
char *path = value_to_string_alloc(&vpath);
free_value(vpath);
if (!path) {
push_value(vm, make_nil());
#endif
break;
}
json_object *root = json_object_from_file(path);
free(path);
if (!root) {
push_value(vm, make_nil());
break;
}
Value v = json_to_fun(root);
push_value(vm, v);
json_object_put(root);
#else
Value vpath = pop_value(vm);
free_value(vpath);
push_value(vm, make_nil());
#endif
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -12,27 +12,30 @@
/* JSON_PARSE */
case OP_JSON_PARSE: {
#ifdef FUN_WITH_JSON
Value text = pop_value(vm);
char *s = value_to_string_alloc(&text);
free_value(text);
if (!s) { push_value(vm, make_nil()); break; }
struct json_tokener *tok = json_tokener_new();
json_object *root = json_tokener_parse_ex(tok, s, (int)strlen(s));
enum json_tokener_error jerr = json_tokener_get_error(tok);
json_tokener_free(tok);
free(s);
if (jerr != json_tokener_success) {
push_value(vm, make_nil());
} else {
Value v = json_to_fun(root);
push_value(vm, v);
json_object_put(root);
}
#else
/* Fallback when JSON is disabled: consume arg, push Nil */
Value drop = pop_value(vm);
free_value(drop);
Value text = pop_value(vm);
char *s = value_to_string_alloc(&text);
free_value(text);
if (!s) {
push_value(vm, make_nil());
#endif
break;
}
struct json_tokener *tok = json_tokener_new();
json_object *root = json_tokener_parse_ex(tok, s, (int)strlen(s));
enum json_tokener_error jerr = json_tokener_get_error(tok);
json_tokener_free(tok);
free(s);
if (jerr != json_tokener_success) {
push_value(vm, make_nil());
} else {
Value v = json_to_fun(root);
push_value(vm, v);
json_object_put(root);
}
#else
/* Fallback when JSON is disabled: consume arg, push Nil */
Value drop = pop_value(vm);
free_value(drop);
push_value(vm, make_nil());
#endif
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -12,21 +12,23 @@
/* JSON_STRINGIFY */
case OP_JSON_STRINGIFY: {
#ifdef FUN_WITH_JSON
Value vpretty = pop_value(vm);
Value any = pop_value(vm);
int pretty = (vpretty.type == VAL_BOOL || vpretty.type == VAL_INT) ? (vpretty.i != 0) : 0;
json_object *j = fun_to_json(&any);
int flags = pretty ? JSON_C_TO_STRING_PRETTY : JSON_C_TO_STRING_PLAIN;
const char *js = json_object_to_json_string_ext(j, flags);
push_value(vm, make_string(js ? js : ""));
json_object_put(j);
free_value(vpretty);
free_value(any);
Value vpretty = pop_value(vm);
Value any = pop_value(vm);
int pretty = (vpretty.type == VAL_BOOL || vpretty.type == VAL_INT) ? (vpretty.i != 0) : 0;
json_object *j = fun_to_json(&any);
int flags = pretty ? JSON_C_TO_STRING_PRETTY : JSON_C_TO_STRING_PLAIN;
const char *js = json_object_to_json_string_ext(j, flags);
push_value(vm, make_string(js ? js : ""));
json_object_put(j);
free_value(vpretty);
free_value(any);
#else
/* Fallback: consume two args, push "null" */
Value vpretty = pop_value(vm); free_value(vpretty);
Value any = pop_value(vm); free_value(any);
push_value(vm, make_string("null"));
/* Fallback: consume two args, push "null" */
Value vpretty = pop_value(vm);
free_value(vpretty);
Value any = pop_value(vm);
free_value(any);
push_value(vm, make_string("null"));
#endif
break;
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -12,26 +12,33 @@
/* JSON_TO_FILE */
case OP_JSON_TO_FILE: {
#ifdef FUN_WITH_JSON
Value vpretty = pop_value(vm);
Value any = pop_value(vm);
Value vpath = pop_value(vm);
char *path = value_to_string_alloc(&vpath);
int pretty = (vpretty.type == VAL_BOOL || vpretty.type == VAL_INT) ? (vpretty.i != 0) : 0;
free_value(vpretty);
free_value(vpath);
if (!path) { free_value(any); push_value(vm, make_int(0)); break; }
json_object *j = fun_to_json(&any);
int flags = pretty ? JSON_C_TO_STRING_PRETTY : JSON_C_TO_STRING_PLAIN;
int rc = json_object_to_file_ext(path, j, flags);
json_object_put(j);
free(path);
Value vpretty = pop_value(vm);
Value any = pop_value(vm);
Value vpath = pop_value(vm);
char *path = value_to_string_alloc(&vpath);
int pretty = (vpretty.type == VAL_BOOL || vpretty.type == VAL_INT) ? (vpretty.i != 0) : 0;
free_value(vpretty);
free_value(vpath);
if (!path) {
free_value(any);
push_value(vm, make_int(rc == 0 ? 1 : 0));
#else
Value vpretty = pop_value(vm); free_value(vpretty);
Value any = pop_value(vm); free_value(any);
Value vpath = pop_value(vm); free_value(vpath);
push_value(vm, make_int(0));
#endif
break;
}
json_object *j = fun_to_json(&any);
int flags = pretty ? JSON_C_TO_STRING_PRETTY : JSON_C_TO_STRING_PLAIN;
int rc = json_object_to_file_ext(path, j, flags);
json_object_put(j);
free(path);
free_value(any);
push_value(vm, make_int(rc == 0 ? 1 : 0));
#else
Value vpretty = pop_value(vm);
free_value(vpretty);
Value any = pop_value(vm);
free_value(any);
Value vpath = pop_value(vm);
free_value(vpath);
push_value(vm, make_int(0));
#endif
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file len.c
* @file len.c
* @brief Implements the OP_LEN opcode for getting the length of arrays or strings in the VM.
*
* This file handles the OP_LEN instruction, which retrieves the length of an array or string.
@ -32,18 +32,18 @@
*/
case OP_LEN: {
Value a = pop_value(vm);
int len = 0;
if (a.type == VAL_STRING) {
len = (int)(a.s ? (int)strlen(a.s) : 0);
} else if (a.type == VAL_ARRAY) {
len = array_length(&a);
if (len < 0) len = 0;
} else {
/* Be lenient: for non-array/non-string, treat length as 0 */
push_value(vm, make_int(0));
}
free_value(a);
push_value(vm, make_int(len));
break;
Value a = pop_value(vm);
int len = 0;
if (a.type == VAL_STRING) {
len = (int)(a.s ? (int)strlen(a.s) : 0);
} else if (a.type == VAL_ARRAY) {
len = array_length(&a);
if (len < 0) len = 0;
} else {
/* Be lenient: for non-array/non-string, treat length as 0 */
push_value(vm, make_int(0));
}
free_value(a);
push_value(vm, make_int(len));
break;
}

View file

@ -10,18 +10,24 @@
*/
/**
* LibreSSL MD5 builtin
*/
* LibreSSL MD5 builtin
*/
case OP_LIBRESSL_MD5: {
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) { push_value(vm, make_string("")); break; }
char *hex = fun_libressl_md5_hex((const unsigned char*)s, strlen(s));
free(s);
if (!hex) { push_value(vm, make_string("")); break; }
Value out = make_string(hex);
free(hex);
push_value(vm, out);
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_md5_hex((const unsigned char *)s, strlen(s));
free(s);
if (!hex) {
push_value(vm, make_string(""));
break;
}
Value out = make_string(hex);
free(hex);
push_value(vm, out);
break;
}

View file

@ -13,15 +13,21 @@
* LibreSSL RIPEMD-160 builtin
*/
case OP_LIBRESSL_RIPEMD160: {
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) { push_value(vm, make_string("")); break; }
char *hex = fun_libressl_ripemd160_hex((const unsigned char*)s, strlen(s));
free(s);
if (!hex) { push_value(vm, make_string("")); break; }
Value out = make_string(hex);
free(hex);
push_value(vm, out);
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_ripemd160_hex((const unsigned char *)s, strlen(s));
free(s);
if (!hex) {
push_value(vm, make_string(""));
break;
}
Value out = make_string(hex);
free(hex);
push_value(vm, out);
break;
}

View file

@ -13,15 +13,21 @@
* LibreSSL SHA-256 builtin
*/
case OP_LIBRESSL_SHA256: {
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) { push_value(vm, make_string("")); break; }
char *hex = fun_libressl_sha256_hex((const unsigned char*)s, strlen(s));
free(s);
if (!hex) { push_value(vm, make_string("")); break; }
Value out = make_string(hex);
free(hex);
push_value(vm, out);
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_sha256_hex((const unsigned char *)s, strlen(s));
free(s);
if (!hex) {
push_value(vm, make_string(""));
break;
}
Value out = make_string(hex);
free(hex);
push_value(vm, out);
break;
}

View file

@ -13,15 +13,21 @@
* LibreSSL SHA-512 builtin
*/
case OP_LIBRESSL_SHA512: {
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) { push_value(vm, make_string("")); break; }
char *hex = fun_libressl_sha512_hex((const unsigned char*)s, strlen(s));
free(s);
if (!hex) { push_value(vm, make_string("")); break; }
Value out = make_string(hex);
free(hex);
push_value(vm, out);
Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata);
free_value(vdata);
if (!s) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_sha512_hex((const unsigned char *)s, strlen(s));
free(s);
if (!hex) {
push_value(vm, make_string(""));
break;
}
Value out = make_string(hex);
free(hex);
push_value(vm, out);
break;
}

View file

@ -14,19 +14,20 @@
*/
case OP_LIBSQL_CLOSE: {
#ifdef FUN_WITH_LIBSQL
Value vh = pop_value(vm);
int hid = (int)vh.i;
free_value(vh);
LibSqlHandle *h = libsql_reg_get(hid);
if (h && h->db) {
sqlite3_close(h->db);
h->db = NULL;
libsql_reg_del(hid);
}
push_value(vm, make_nil());
Value vh = pop_value(vm);
int hid = (int)vh.i;
free_value(vh);
LibSqlHandle *h = libsql_reg_get(hid);
if (h && h->db) {
sqlite3_close(h->db);
h->db = NULL;
libsql_reg_del(hid);
}
push_value(vm, make_nil());
#else
Value v = pop_value(vm); free_value(v);
push_value(vm, make_nil());
Value v = pop_value(vm);
free_value(v);
push_value(vm, make_nil());
#endif
break;
break;
}

View file

@ -14,23 +14,29 @@
*/
case OP_LIBSQL_EXEC: {
#ifdef FUN_WITH_LIBSQL
Value vsql = pop_value(vm);
Value vh = pop_value(vm);
int hid = (int)vh.i;
char *sql = value_to_string_alloc(&vsql);
free_value(vh);
free_value(vsql);
LibSqlHandle *h = libsql_reg_get(hid);
if (!h || !h->db || !sql) { if (sql) free(sql); push_value(vm, make_int(SQLITE_MISUSE)); break; }
char *errmsg = NULL;
int rc = sqlite3_exec(h->db, sql, NULL, NULL, &errmsg);
if (errmsg) sqlite3_free(errmsg);
free(sql);
push_value(vm, make_int(rc));
#else
Value v1 = pop_value(vm); free_value(v1);
Value v2 = pop_value(vm); free_value(v2);
push_value(vm, make_int(-1));
#endif
Value vsql = pop_value(vm);
Value vh = pop_value(vm);
int hid = (int)vh.i;
char *sql = value_to_string_alloc(&vsql);
free_value(vh);
free_value(vsql);
LibSqlHandle *h = libsql_reg_get(hid);
if (!h || !h->db || !sql) {
if (sql) free(sql);
push_value(vm, make_int(SQLITE_MISUSE));
break;
}
char *errmsg = NULL;
int rc = sqlite3_exec(h->db, sql, NULL, NULL, &errmsg);
if (errmsg) sqlite3_free(errmsg);
free(sql);
push_value(vm, make_int(rc));
#else
Value v1 = pop_value(vm);
free_value(v1);
Value v2 = pop_value(vm);
free_value(v2);
push_value(vm, make_int(-1));
#endif
break;
}

View file

@ -14,24 +14,32 @@
*/
case OP_LIBSQL_OPEN: {
#ifdef FUN_WITH_LIBSQL
Value vpath = pop_value(vm);
char *path = value_to_string_alloc(&vpath);
free_value(vpath);
if (!path) { push_value(vm, make_int(0)); break; }
sqlite3 *db = NULL;
int rc = sqlite3_open(path, &db);
free(path);
if (rc != SQLITE_OK || !db) {
if (db) sqlite3_close(db);
push_value(vm, make_int(0));
break;
}
LibSqlHandle *h = libsql_reg_add(db);
if (!h) { sqlite3_close(db); push_value(vm, make_int(0)); break; }
push_value(vm, make_int(h->id));
#else
Value v = pop_value(vm); free_value(v);
Value vpath = pop_value(vm);
char *path = value_to_string_alloc(&vpath);
free_value(vpath);
if (!path) {
push_value(vm, make_int(0));
#endif
break;
}
sqlite3 *db = NULL;
int rc = sqlite3_open(path, &db);
free(path);
if (rc != SQLITE_OK || !db) {
if (db) sqlite3_close(db);
push_value(vm, make_int(0));
break;
}
LibSqlHandle *h = libsql_reg_add(db);
if (!h) {
sqlite3_close(db);
push_value(vm, make_int(0));
break;
}
push_value(vm, make_int(h->id));
#else
Value v = pop_value(vm);
free_value(v);
push_value(vm, make_int(0));
#endif
break;
}

View file

@ -14,47 +14,63 @@
*/
case OP_LIBSQL_QUERY: {
#ifdef FUN_WITH_LIBSQL
Value vsql = pop_value(vm);
Value vh = pop_value(vm);
int hid = (int)vh.i;
char *sql = value_to_string_alloc(&vsql);
free_value(vh);
free_value(vsql);
LibSqlHandle *h = libsql_reg_get(hid);
if (!h || !h->db || !sql) { if (sql) free(sql); push_value(vm, make_array_from_values(NULL, 0)); break; }
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(h->db, sql, -1, &stmt, NULL) != SQLITE_OK) {
free(sql);
push_value(vm, make_array_from_values(NULL, 0));
break;
}
free(sql);
Value rows = make_array_from_values(NULL, 0);
int ncols = sqlite3_column_count(stmt);
while (sqlite3_step(stmt) == SQLITE_ROW) {
Value row = make_map_empty();
for (int i = 0; i < ncols; i++) {
const char *name = sqlite3_column_name(stmt, i);
int type = sqlite3_column_type(stmt, i);
Value kv;
switch (type) {
case SQLITE_INTEGER: kv = make_int((int64_t)sqlite3_column_int64(stmt, i)); break;
case SQLITE_FLOAT: kv = make_float(sqlite3_column_double(stmt, i)); break;
case SQLITE_TEXT: kv = make_string((const char*)sqlite3_column_text(stmt, i)); break;
case SQLITE_NULL: kv = make_nil(); break;
default: kv = make_nil(); break; /* ignore blobs for now */
}
(void)map_set(&row, name ? name : "", kv);
}
(void)array_push(&rows, row);
/* Do NOT free 'row' here; owned by rows array. */
}
sqlite3_finalize(stmt);
push_value(vm, rows);
#else
Value v1 = pop_value(vm); free_value(v1);
Value v2 = pop_value(vm); free_value(v2);
Value vsql = pop_value(vm);
Value vh = pop_value(vm);
int hid = (int)vh.i;
char *sql = value_to_string_alloc(&vsql);
free_value(vh);
free_value(vsql);
LibSqlHandle *h = libsql_reg_get(hid);
if (!h || !h->db || !sql) {
if (sql) free(sql);
push_value(vm, make_array_from_values(NULL, 0));
#endif
break;
}
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(h->db, sql, -1, &stmt, NULL) != SQLITE_OK) {
free(sql);
push_value(vm, make_array_from_values(NULL, 0));
break;
}
free(sql);
Value rows = make_array_from_values(NULL, 0);
int ncols = sqlite3_column_count(stmt);
while (sqlite3_step(stmt) == SQLITE_ROW) {
Value row = make_map_empty();
for (int i = 0; i < ncols; i++) {
const char *name = sqlite3_column_name(stmt, i);
int type = sqlite3_column_type(stmt, i);
Value kv;
switch (type) {
case SQLITE_INTEGER:
kv = make_int((int64_t)sqlite3_column_int64(stmt, i));
break;
case SQLITE_FLOAT:
kv = make_float(sqlite3_column_double(stmt, i));
break;
case SQLITE_TEXT:
kv = make_string((const char *)sqlite3_column_text(stmt, i));
break;
case SQLITE_NULL:
kv = make_nil();
break;
default:
kv = make_nil();
break; /* ignore blobs for now */
}
(void)map_set(&row, name ? name : "", kv);
}
(void)array_push(&rows, row);
/* Do NOT free 'row' here; owned by rows array. */
}
sqlite3_finalize(stmt);
push_value(vm, rows);
#else
Value v1 = pop_value(vm);
free_value(v1);
Value v2 = pop_value(vm);
free_value(v2);
push_value(vm, make_array_from_values(NULL, 0));
#endif
break;
}

View file

@ -1,5 +1,5 @@
/**
* This file is part of the Fun programming language.
* This file is part of the Fun programming language.
* https://fun-lang.xyz/
*
* Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -8,7 +8,7 @@
*/
case OP_LINE: {
/* operand holds the source line number */
vm->current_line = inst.operand;
break;
/* operand holds the source line number */
vm->current_line = inst.operand;
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file and.c
* @file and.c
* @brief Implements the OP_AND opcode for logical AND in the VM.
*
* This file handles the OP_AND instruction, which performs a logical AND operation
@ -32,11 +32,11 @@
*/
case OP_AND: {
Value b = pop_value(vm);
Value a = pop_value(vm);
int res = value_is_truthy(&a) && value_is_truthy(&b);
free_value(a);
free_value(b);
push_value(vm, make_bool(res));
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
int res = value_is_truthy(&a) && value_is_truthy(&b);
free_value(a);
free_value(b);
push_value(vm, make_bool(res));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file eq.c
* @file eq.c
* @brief Implements the OP_EQ opcode for equality comparison in the VM.
*
* This file handles the OP_EQ instruction, which checks if two values are equal.
@ -32,29 +32,42 @@
*/
case OP_EQ: {
Value b = pop_value(vm);
Value a = pop_value(vm);
int eq = 0;
if (a.type == b.type) {
switch (a.type) {
case VAL_INT: eq = (a.i == b.i); break;
case VAL_BOOL: eq = ((a.i != 0) == (b.i != 0)); break;
case VAL_STRING: eq = (a.s && b.s) ? (strcmp(a.s, b.s) == 0) : (a.s == b.s); break;
case VAL_FUNCTION: eq = (a.fn == b.fn); break;
case VAL_NIL: eq = 1; break;
default: eq = 0; break;
}
} else {
/* interop: bool vs int (0/1) */
if ((a.type == VAL_BOOL && b.type == VAL_INT) || (a.type == VAL_INT && b.type == VAL_BOOL)) {
int ai = (a.type == VAL_BOOL) ? (a.i != 0) : (a.i != 0);
int bi = (b.type == VAL_BOOL) ? (b.i != 0) : (b.i != 0);
eq = (ai == bi);
} else {
eq = 0;
}
Value b = pop_value(vm);
Value a = pop_value(vm);
int eq = 0;
if (a.type == b.type) {
switch (a.type) {
case VAL_INT:
eq = (a.i == b.i);
break;
case VAL_BOOL:
eq = ((a.i != 0) == (b.i != 0));
break;
case VAL_STRING:
eq = (a.s && b.s) ? (strcmp(a.s, b.s) == 0) : (a.s == b.s);
break;
case VAL_FUNCTION:
eq = (a.fn == b.fn);
break;
case VAL_NIL:
eq = 1;
break;
default:
eq = 0;
break;
}
push_value(vm, make_bool(eq));
free_value(a); free_value(b);
break;
} else {
/* interop: bool vs int (0/1) */
if ((a.type == VAL_BOOL && b.type == VAL_INT) || (a.type == VAL_INT && b.type == VAL_BOOL)) {
int ai = (a.type == VAL_BOOL) ? (a.i != 0) : (a.i != 0);
int bi = (b.type == VAL_BOOL) ? (b.i != 0) : (b.i != 0);
eq = (ai == bi);
} else {
eq = 0;
}
}
push_value(vm, make_bool(eq));
free_value(a);
free_value(b);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file gt.c
* @file gt.c
* @brief Implements the OP_GT opcode for greater-than comparison in the VM.
*
* This file handles the OP_GT instruction, which checks if the first value is greater than the second.
@ -33,13 +33,14 @@
*/
case OP_GT: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: GT expects ints\n");
exit(1);
}
push_value(vm, make_int(a.i > b.i ? 1 : 0));
free_value(a); free_value(b);
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: GT expects ints\n");
exit(1);
}
push_value(vm, make_int(a.i > b.i ? 1 : 0));
free_value(a);
free_value(b);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file gte.c
* @file gte.c
* @brief Implements the OP_GTE opcode for greater-than-or-equal comparison in the VM.
*
* This file handles the OP_GTE instruction, which checks if the first value is greater than or equal to the second.
@ -32,13 +32,14 @@
*/
case OP_GTE: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: GTE expects ints\n");
exit(1);
}
push_value(vm, make_int(a.i >= b.i ? 1 : 0));
free_value(a); free_value(b);
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: GTE expects ints\n");
exit(1);
}
push_value(vm, make_int(a.i >= b.i ? 1 : 0));
free_value(a);
free_value(b);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file lt.c
* @file lt.c
* @brief Implements the OP_LT opcode for less-than comparison in the VM.
*
* This file handles the OP_LT instruction, which checks if the first value is less than the second.
@ -32,16 +32,16 @@
*/
case OP_LT: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
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);
}
Value res = make_int(a.i < b.i ? 1 : 0);
free_value(a);
free_value(b);
push_value(vm, res);
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
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);
}
Value res = make_int(a.i < b.i ? 1 : 0);
free_value(a);
free_value(b);
push_value(vm, res);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file lte.c
* @file lte.c
* @brief Implements the OP_LTE opcode for less-than-or-equal comparison in the VM.
*
* This file handles the OP_LTE instruction, which checks if the first value is less than or equal to the second.
@ -32,15 +32,15 @@
*/
case OP_LTE: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: LTE expects ints\n");
exit(1);
}
Value res = make_int(a.i <= b.i ? 1 : 0);
free_value(a);
free_value(b);
push_value(vm, res);
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "Runtime type error: LTE expects ints\n");
exit(1);
}
Value res = make_int(a.i <= b.i ? 1 : 0);
free_value(a);
free_value(b);
push_value(vm, res);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file neq.c
* @file neq.c
* @brief Implements the OP_NEQ opcode for inequality comparison in the VM.
*
* This file handles the OP_NEQ instruction, which checks if two values are not equal.
@ -32,29 +32,42 @@
*/
case OP_NEQ: {
Value b = pop_value(vm);
Value a = pop_value(vm);
int neq = 1;
if (a.type == b.type) {
switch (a.type) {
case VAL_INT: neq = (a.i != b.i); break;
case VAL_BOOL: neq = ((a.i != 0) != (b.i != 0)); break;
case VAL_STRING: neq = (a.s && b.s) ? (strcmp(a.s, b.s) != 0) : (a.s != b.s); break;
case VAL_FUNCTION: neq = (a.fn != b.fn); break;
case VAL_NIL: neq = 0; break;
default: neq = 1; break;
}
} else {
/* interop: bool vs int (0/1) */
if ((a.type == VAL_BOOL && b.type == VAL_INT) || (a.type == VAL_INT && b.type == VAL_BOOL)) {
int ai = (a.type == VAL_BOOL) ? (a.i != 0) : (a.i != 0);
int bi = (b.type == VAL_BOOL) ? (b.i != 0) : (b.i != 0);
neq = (ai != bi);
} else {
neq = 1;
}
Value b = pop_value(vm);
Value a = pop_value(vm);
int neq = 1;
if (a.type == b.type) {
switch (a.type) {
case VAL_INT:
neq = (a.i != b.i);
break;
case VAL_BOOL:
neq = ((a.i != 0) != (b.i != 0));
break;
case VAL_STRING:
neq = (a.s && b.s) ? (strcmp(a.s, b.s) != 0) : (a.s != b.s);
break;
case VAL_FUNCTION:
neq = (a.fn != b.fn);
break;
case VAL_NIL:
neq = 0;
break;
default:
neq = 1;
break;
}
push_value(vm, make_bool(neq));
free_value(a); free_value(b);
break;
} else {
/* interop: bool vs int (0/1) */
if ((a.type == VAL_BOOL && b.type == VAL_INT) || (a.type == VAL_INT && b.type == VAL_BOOL)) {
int ai = (a.type == VAL_BOOL) ? (a.i != 0) : (a.i != 0);
int bi = (b.type == VAL_BOOL) ? (b.i != 0) : (b.i != 0);
neq = (ai != bi);
} else {
neq = 1;
}
}
push_value(vm, make_bool(neq));
free_value(a);
free_value(b);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file not.c
* @file not.c
* @brief Implements the OP_NOT opcode for logical NOT in the VM.
*
* This file handles the OP_NOT instruction, which performs a logical NOT operation
@ -32,9 +32,9 @@
*/
case OP_NOT: {
Value v = pop_value(vm);
int res = !value_is_truthy(&v);
free_value(v);
push_value(vm, make_bool(res));
break;
Value v = pop_value(vm);
int res = !value_is_truthy(&v);
free_value(v);
push_value(vm, make_bool(res));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file or.c
* @file or.c
* @brief Implements the OP_OR opcode for logical OR in the VM.
*
* This file handles the OP_OR instruction, which performs a logical OR operation
@ -32,11 +32,11 @@
*/
case OP_OR: {
Value b = pop_value(vm);
Value a = pop_value(vm);
int res = value_is_truthy(&a) || value_is_truthy(&b);
free_value(a);
free_value(b);
push_value(vm, make_bool(res));
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
int res = value_is_truthy(&a) || value_is_truthy(&b);
free_value(a);
free_value(b);
push_value(vm, make_bool(res));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file has_key.c
* @file has_key.c
* @brief Implements the OP_HAS_KEY opcode for map key checking in the VM.
*
* This file handles the OP_HAS_KEY instruction, which checks if a map contains
@ -26,11 +26,15 @@
*/
case OP_HAS_KEY: {
Value key = pop_value(vm);
Value m = pop_value(vm);
if (m.type != VAL_MAP || key.type != VAL_STRING) { fprintf(stderr, "HAS_KEY expects (map, string)\n"); exit(1); }
int ok = map_has(&m, key.s ? key.s : "");
free_value(m); free_value(key);
push_value(vm, make_int(ok ? 1 : 0));
break;
Value key = pop_value(vm);
Value m = pop_value(vm);
if (m.type != VAL_MAP || key.type != VAL_STRING) {
fprintf(stderr, "HAS_KEY expects (map, string)\n");
exit(1);
}
int ok = map_has(&m, key.s ? key.s : "");
free_value(m);
free_value(key);
push_value(vm, make_int(ok ? 1 : 0));
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file keys.c
* @file keys.c
* @brief Implements the OP_KEYS opcode for retrieving map keys in the VM.
*
* This file handles the OP_KEYS instruction, which retrieves the keys of a map
@ -32,10 +32,13 @@
*/
case OP_KEYS: {
Value m = pop_value(vm);
if (m.type != VAL_MAP) { fprintf(stderr, "KEYS expects map\n"); exit(1); }
Value arr = map_keys_array(&m);
free_value(m);
push_value(vm, arr);
break;
Value m = pop_value(vm);
if (m.type != VAL_MAP) {
fprintf(stderr, "KEYS expects map\n");
exit(1);
}
Value arr = map_keys_array(&m);
free_value(m);
push_value(vm, arr);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file make_map.c
* @file make_map.c
* @brief Implements the OP_MAKE_MAP opcode for creating maps in the VM.
*
* This file handles the OP_MAKE_MAP instruction, which pops `pairs` key-value pairs
@ -33,20 +33,26 @@
* @date 2025-10-16
*/
case OP_MAKE_MAP: {
int pairs = inst.operand;
if (pairs < 0) { fprintf(stderr, "MAKE_MAP invalid pair count\n"); exit(1); }
Value m = make_map_empty();
for (int i = 0; i < pairs; ++i) {
Value val = pop_value(vm);
Value key = pop_value(vm);
if (key.type != VAL_STRING) { fprintf(stderr, "Map literal keys must be strings\n"); exit(1); }
if (!map_set(&m, key.s ? key.s : "", val)) {
fprintf(stderr, "Map literal set failed\n"); exit(1);
}
free_value(key);
int pairs = inst.operand;
if (pairs < 0) {
fprintf(stderr, "MAKE_MAP invalid pair count\n");
exit(1);
}
Value m = make_map_empty();
for (int i = 0; i < pairs; ++i) {
Value val = pop_value(vm);
Value key = pop_value(vm);
if (key.type != VAL_STRING) {
fprintf(stderr, "Map literal keys must be strings\n");
exit(1);
}
push_value(vm, m);
break;
if (!map_set(&m, key.s ? key.s : "", val)) {
fprintf(stderr, "Map literal set failed\n");
exit(1);
}
free_value(key);
}
push_value(vm, m);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file values.c
* @file values.c
* @brief Implements the OP_VALUES opcode for retrieving map values in the VM.
*
* This file handles the OP_VALUES instruction, which retrieves the values of a map
@ -32,10 +32,13 @@
*/
case OP_VALUES: {
Value m = pop_value(vm);
if (m.type != VAL_MAP) { fprintf(stderr, "VALUES expects map\n"); exit(1); }
Value arr = map_values_array(&m);
free_value(m);
push_value(vm, arr);
break;
Value m = pop_value(vm);
if (m.type != VAL_MAP) {
fprintf(stderr, "VALUES expects map\n");
exit(1);
}
Value arr = map_values_array(&m);
free_value(m);
push_value(vm, arr);
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file abs.c
* @file abs.c
* @brief Implements the OP_ABS opcode for absolute value in the VM.
*
* This file handles the OP_ABS instruction, which computes the absolute value
@ -26,11 +26,14 @@
*/
case OP_ABS: {
Value x = pop_value(vm);
if (x.type != VAL_INT) { fprintf(stderr, "ABS expects int\n"); exit(1); }
int64_t v = x.i;
if (v < 0) v = -v;
push_value(vm, make_int(v));
free_value(x);
break;
Value x = pop_value(vm);
if (x.type != VAL_INT) {
fprintf(stderr, "ABS expects int\n");
exit(1);
}
int64_t v = x.i;
if (v < 0) v = -v;
push_value(vm, make_int(v));
free_value(x);
break;
}

View file

@ -10,34 +10,34 @@
*/
/**
* @file ceil.c
* @file ceil.c
* @brief Implements the OP_CEIL opcode using C99 math.h ceil().
*/
#include <math.h>
case OP_CEIL: {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
/* ceil(n) == n for integers */
push_value(vm, make_int(v.i));
free_value(v);
} else if (v.type == VAL_FLOAT) {
double r = ceil(v.d);
if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r;
if ((double)ii == r) {
push_value(vm, make_int(ii));
} else {
push_value(vm, make_float(r));
}
} else {
push_value(vm, make_float(r));
}
free_value(v);
Value v = pop_value(vm);
if (v.type == VAL_INT) {
/* ceil(n) == n for integers */
push_value(vm, make_int(v.i));
free_value(v);
} else if (v.type == VAL_FLOAT) {
double r = ceil(v.d);
if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r;
if ((double)ii == r) {
push_value(vm, make_int(ii));
} else {
push_value(vm, make_float(r));
}
} else {
fprintf(stderr, "Runtime type error: CEIL expects number, got %s\n", value_type_name(v.type));
exit(1);
push_value(vm, make_float(r));
}
break;
free_value(v);
} else {
fprintf(stderr, "Runtime type error: CEIL expects number, got %s\n", value_type_name(v.type));
exit(1);
}
break;
}

View file

@ -8,7 +8,7 @@
*/
/**
* @file clamp.c
* @file clamp.c
* @brief Implements the OP_CLAMP opcode for value clamping in the VM.
*
* This file handles the OP_CLAMP instruction, which clamps a value between
@ -27,19 +27,19 @@
*/
case OP_CLAMP: {
Value hi = pop_value(vm);
Value lo = pop_value(vm);
Value x = pop_value(vm);
if (x.type != VAL_INT || lo.type != VAL_INT || hi.type != VAL_INT) {
fprintf(stderr, "CLAMP expects ints\n");
exit(1);
}
int64_t v = x.i;
if (v < lo.i) v = lo.i;
if (v > hi.i) v = hi.i;
push_value(vm, make_int(v));
free_value(x);
free_value(lo);
free_value(hi);
break;
Value hi = pop_value(vm);
Value lo = pop_value(vm);
Value x = pop_value(vm);
if (x.type != VAL_INT || lo.type != VAL_INT || hi.type != VAL_INT) {
fprintf(stderr, "CLAMP expects ints\n");
exit(1);
}
int64_t v = x.i;
if (v < lo.i) v = lo.i;
if (v > hi.i) v = hi.i;
push_value(vm, make_int(v));
free_value(x);
free_value(lo);
free_value(hi);
break;
}

View file

@ -10,22 +10,22 @@
*/
/**
* @file cos.c
* @file cos.c
* @brief Implements the OP_COS opcode using C99 math.h cos().
*/
#include <math.h>
case OP_COS: {
Value v = pop_value(vm);
if (v.type == VAL_INT || v.type == VAL_FLOAT) {
double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i;
double r = cos(x);
push_value(vm, make_float(r));
free_value(v);
} else {
fprintf(stderr, "Runtime type error: COS expects number, got %s\n", value_type_name(v.type));
exit(1);
}
break;
Value v = pop_value(vm);
if (v.type == VAL_INT || v.type == VAL_FLOAT) {
double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i;
double r = cos(x);
push_value(vm, make_float(r));
free_value(v);
} else {
fprintf(stderr, "Runtime type error: COS expects number, got %s\n", value_type_name(v.type));
exit(1);
}
break;
}

View file

@ -10,22 +10,22 @@
*/
/**
* @file exp.c
* @file exp.c
* @brief Implements the OP_EXP opcode using C99 math.h exp().
*/
#include <math.h>
case OP_EXP: {
Value v = pop_value(vm);
if (v.type == VAL_INT || v.type == VAL_FLOAT) {
double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i;
double r = exp(x);
push_value(vm, make_float(r));
free_value(v);
} else {
fprintf(stderr, "Runtime type error: EXP expects number, got %s\n", value_type_name(v.type));
exit(1);
}
break;
Value v = pop_value(vm);
if (v.type == VAL_INT || v.type == VAL_FLOAT) {
double x = (v.type == VAL_FLOAT) ? v.d : (double)v.i;
double r = exp(x);
push_value(vm, make_float(r));
free_value(v);
} else {
fprintf(stderr, "Runtime type error: EXP expects number, got %s\n", value_type_name(v.type));
exit(1);
}
break;
}

View file

@ -10,34 +10,34 @@
*/
/**
* @file floor.c
* @file floor.c
* @brief Implements the OP_FLOOR opcode using C99 math.h floor().
*/
#include <math.h>
case OP_FLOOR: {
Value v = pop_value(vm);
if (v.type == VAL_INT) {
/* floor(n) == n for integers */
push_value(vm, make_int(v.i));
free_value(v);
} else if (v.type == VAL_FLOAT) {
double r = floor(v.d);
if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r;
if ((double)ii == r) {
push_value(vm, make_int(ii));
} else {
push_value(vm, make_float(r));
}
} else {
push_value(vm, make_float(r));
}
free_value(v);
Value v = pop_value(vm);
if (v.type == VAL_INT) {
/* floor(n) == n for integers */
push_value(vm, make_int(v.i));
free_value(v);
} else if (v.type == VAL_FLOAT) {
double r = floor(v.d);
if (r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r;
if ((double)ii == r) {
push_value(vm, make_int(ii));
} else {
push_value(vm, make_float(r));
}
} else {
fprintf(stderr, "Runtime type error: FLOOR expects number, got %s\n", value_type_name(v.type));
exit(1);
push_value(vm, make_float(r));
}
break;
free_value(v);
} else {
fprintf(stderr, "Runtime type error: FLOOR expects number, got %s\n", value_type_name(v.type));
exit(1);
}
break;
}

View file

@ -10,7 +10,7 @@
*/
/**
* @file fmax.c
* @file fmax.c
* @brief Implements the OP_FMAX opcode using C99 math.h fmax().
* Accepts int or float; follows IEEE-754 NaN handling per fmax.
*/
@ -18,24 +18,28 @@
#include <math.h>
case OP_FMAX: {
Value b = pop_value(vm);
Value a = pop_value(vm);
if (!((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT))) {
fprintf(stderr, "Runtime type error: FMAX expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
double r = fmax(da, db);
Value out;
if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r;
if ((double)ii == r) out = make_int(ii); else out = make_float(r);
} else {
out = make_float(r);
}
free_value(a); free_value(b);
push_value(vm, out);
break;
Value b = pop_value(vm);
Value a = pop_value(vm);
if (!((a.type == VAL_INT || a.type == VAL_FLOAT) && (b.type == VAL_INT || b.type == VAL_FLOAT))) {
fprintf(stderr, "Runtime type error: FMAX expects numbers, got %s and %s\n",
value_type_name(a.type), value_type_name(b.type));
exit(1);
}
double da = (a.type == VAL_FLOAT) ? a.d : (double)a.i;
double db = (b.type == VAL_FLOAT) ? b.d : (double)b.i;
double r = fmax(da, db);
Value out;
if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r;
if ((double)ii == r)
out = make_int(ii);
else
out = make_float(r);
} else {
out = make_float(r);
}
free_value(a);
free_value(b);
push_value(vm, out);
break;
}

Some files were not shown because too many files have changed in this diff Show more