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:
parent
33001a7ad2
commit
03b2532474
237 changed files with 17550 additions and 13911 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
130
src/vm/cast.c
130
src/vm/cast.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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) */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
30
src/vm/len.c
30
src/vm/len.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue