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
|
|
@ -12,46 +12,46 @@
|
|||
/* array utilities */
|
||||
|
||||
int array_contains(const Value *v, const Value *needle) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
int eq = value_equals(&item, needle);
|
||||
free_value(item);
|
||||
if (eq) return 1;
|
||||
}
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
int eq = value_equals(&item, needle);
|
||||
free_value(item);
|
||||
if (eq) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int array_index_of(const Value *v, const Value *needle) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
int eq = value_equals(&item, needle);
|
||||
free_value(item);
|
||||
if (eq) return i;
|
||||
}
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
int eq = value_equals(&item, needle);
|
||||
free_value(item);
|
||||
if (eq) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void array_clear(Value *v) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return;
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
free_value(item);
|
||||
}
|
||||
}
|
||||
/* internal clear uses public API to reset to zero length */
|
||||
/* Since we don't expose capacity, emulate by popping elements */
|
||||
Value out;
|
||||
while (array_pop(v, &out)) {
|
||||
free_value(out);
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return;
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
free_value(item);
|
||||
}
|
||||
}
|
||||
/* internal clear uses public API to reset to zero length */
|
||||
/* Since we don't expose capacity, emulate by popping elements */
|
||||
Value out;
|
||||
while (array_pop(v, &out)) {
|
||||
free_value(out);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
585
src/bytecode.c
585
src/bytecode.c
|
|
@ -8,235 +8,400 @@
|
|||
*/
|
||||
|
||||
#include "bytecode.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
Bytecode *bytecode_new(void) {
|
||||
Bytecode *bc = (Bytecode*)malloc(sizeof(Bytecode));
|
||||
bc->instructions = NULL;
|
||||
bc->instr_count = 0;
|
||||
bc->constants = NULL;
|
||||
bc->const_count = 0;
|
||||
bc->name = NULL;
|
||||
bc->source_file = NULL;
|
||||
return bc;
|
||||
Bytecode *bc = (Bytecode *)malloc(sizeof(Bytecode));
|
||||
bc->instructions = NULL;
|
||||
bc->instr_count = 0;
|
||||
bc->constants = NULL;
|
||||
bc->const_count = 0;
|
||||
bc->name = NULL;
|
||||
bc->source_file = NULL;
|
||||
return bc;
|
||||
}
|
||||
|
||||
int bytecode_add_constant(Bytecode *bc, Value v) {
|
||||
bc->constants = (Value*)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1));
|
||||
bc->constants[bc->const_count] = copy_value(&v);
|
||||
return bc->const_count++;
|
||||
bc->constants = (Value *)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1));
|
||||
bc->constants[bc->const_count] = copy_value(&v);
|
||||
return bc->const_count++;
|
||||
}
|
||||
|
||||
int bytecode_add_instruction(Bytecode *bc, OpCode op, int32_t operand) {
|
||||
bc->instructions = (Instruction*)realloc(bc->instructions, sizeof(Instruction) * (bc->instr_count + 1));
|
||||
bc->instructions[bc->instr_count].op = op;
|
||||
bc->instructions[bc->instr_count].operand = operand;
|
||||
return bc->instr_count++;
|
||||
bc->instructions = (Instruction *)realloc(bc->instructions, sizeof(Instruction) * (bc->instr_count + 1));
|
||||
bc->instructions[bc->instr_count].op = op;
|
||||
bc->instructions[bc->instr_count].operand = operand;
|
||||
return bc->instr_count++;
|
||||
}
|
||||
|
||||
void bytecode_set_operand(Bytecode *bc, int idx, int32_t operand) {
|
||||
if (idx >= 0 && idx < bc->instr_count) {
|
||||
bc->instructions[idx].operand = operand;
|
||||
}
|
||||
if (idx >= 0 && idx < bc->instr_count) {
|
||||
bc->instructions[idx].operand = operand;
|
||||
}
|
||||
}
|
||||
|
||||
void bytecode_free(Bytecode *bc) {
|
||||
if (!bc) return;
|
||||
for (int i = 0; i < bc->const_count; ++i) {
|
||||
free_value(bc->constants[i]);
|
||||
}
|
||||
free(bc->constants);
|
||||
free(bc->instructions);
|
||||
if (bc->name) free((void*)bc->name);
|
||||
if (bc->source_file) free((void*)bc->source_file);
|
||||
free(bc);
|
||||
if (!bc) return;
|
||||
for (int i = 0; i < bc->const_count; ++i) {
|
||||
free_value(bc->constants[i]);
|
||||
}
|
||||
free(bc->constants);
|
||||
free(bc->instructions);
|
||||
if (bc->name) free((void *)bc->name);
|
||||
if (bc->source_file) free((void *)bc->source_file);
|
||||
free(bc);
|
||||
}
|
||||
|
||||
static const char *opcode_name(OpCode op) {
|
||||
switch (op) {
|
||||
case OP_NOP: return "NOP";
|
||||
case OP_LOAD_CONST: return "LOAD_CONST";
|
||||
case OP_LOAD_LOCAL: return "LOAD_LOCAL";
|
||||
case OP_STORE_LOCAL: return "STORE_LOCAL";
|
||||
case OP_LOAD_GLOBAL: return "LOAD_GLOBAL";
|
||||
case OP_STORE_GLOBAL: return "STORE_GLOBAL";
|
||||
case OP_ADD: return "ADD";
|
||||
case OP_SUB: return "SUB";
|
||||
case OP_MUL: return "MUL";
|
||||
case OP_DIV: return "DIV";
|
||||
case OP_LT: return "LT";
|
||||
case OP_LTE: return "LTE";
|
||||
case OP_GT: return "GT";
|
||||
case OP_GTE: return "GTE";
|
||||
case OP_EQ: return "EQ";
|
||||
case OP_NEQ: return "NEQ";
|
||||
case OP_POP: return "POP";
|
||||
case OP_JUMP: return "JUMP";
|
||||
case OP_JUMP_IF_FALSE: return "JUMP_IF_FALSE";
|
||||
case OP_CALL: return "CALL";
|
||||
case OP_RETURN: return "RETURN";
|
||||
case OP_PRINT: return "PRINT";
|
||||
case OP_ECHO: return "ECHO";
|
||||
case OP_HALT: return "HALT";
|
||||
case OP_MOD: return "MOD";
|
||||
case OP_AND: return "AND";
|
||||
case OP_OR: return "OR";
|
||||
case OP_NOT: return "NOT";
|
||||
case OP_DUP: return "DUP";
|
||||
case OP_SWAP: return "SWAP";
|
||||
case OP_MAKE_ARRAY: return "MAKE_ARRAY";
|
||||
case OP_INDEX_GET: return "INDEX_GET";
|
||||
case OP_INDEX_SET: return "INDEX_SET";
|
||||
case OP_LEN: return "LEN";
|
||||
case OP_PUSH: return "ARR_PUSH";
|
||||
case OP_APOP: return "ARR_POP";
|
||||
case OP_SET: return "ARR_SET";
|
||||
case OP_INSERT: return "ARR_INSERT";
|
||||
case OP_REMOVE: return "ARR_REMOVE";
|
||||
case OP_SLICE: return "SLICE";
|
||||
case OP_TO_NUMBER: return "TO_NUMBER";
|
||||
case OP_TO_STRING: return "TO_STRING";
|
||||
case OP_TYPEOF: return "TYPEOF";
|
||||
case OP_CAST: return "CAST";
|
||||
case OP_SPLIT: return "SPLIT";
|
||||
case OP_JOIN: return "JOIN";
|
||||
case OP_SUBSTR: return "SUBSTR";
|
||||
case OP_FIND: return "FIND";
|
||||
case OP_REGEX_MATCH: return "REGEX_MATCH";
|
||||
case OP_REGEX_SEARCH: return "REGEX_SEARCH";
|
||||
case OP_REGEX_REPLACE: return "REGEX_REPLACE";
|
||||
case OP_CONTAINS: return "CONTAINS";
|
||||
case OP_INDEX_OF: return "INDEX_OF";
|
||||
case OP_CLEAR: return "CLEAR";
|
||||
case OP_ENUMERATE: return "ENUMERATE";
|
||||
case OP_ZIP: return "ZIP";
|
||||
case OP_MIN: return "MIN";
|
||||
case OP_MAX: return "MAX";
|
||||
case OP_CLAMP: return "CLAMP";
|
||||
case OP_ABS: return "ABS";
|
||||
case OP_POW: return "POW";
|
||||
case OP_RANDOM_SEED: return "RANDOM_SEED";
|
||||
case OP_RANDOM_INT: return "RANDOM_INT";
|
||||
case OP_MAKE_MAP: return "MAKE_MAP";
|
||||
case OP_KEYS: return "KEYS";
|
||||
case OP_VALUES: return "VALUES";
|
||||
case OP_HAS_KEY: return "HAS_KEY";
|
||||
case OP_READ_FILE: return "READ_FILE";
|
||||
case OP_WRITE_FILE: return "WRITE_FILE";
|
||||
case OP_ENV: return "ENV";
|
||||
case OP_INPUT_LINE: return "INPUT_LINE";
|
||||
case OP_PROC_RUN: return "PROC_RUN";
|
||||
case OP_PROC_SYSTEM: return "PROC_SYSTEM";
|
||||
case OP_TIME_NOW_MS: return "TIME_NOW_MS";
|
||||
case OP_CLOCK_MONO_MS: return "CLOCK_MONO_MS";
|
||||
case OP_DATE_FORMAT: return "DATE_FORMAT";
|
||||
case OP_ENV_ALL: return "ENV_ALL";
|
||||
case OP_FUN_VERSION: return "FUN_VERSION";
|
||||
case OP_THREAD_SPAWN: return "THREAD_SPAWN";
|
||||
case OP_THREAD_JOIN: return "THREAD_JOIN";
|
||||
case OP_SLEEP_MS: return "SLEEP_MS";
|
||||
case OP_RANDOM_NUMBER: return "RANDOM_NUMBER";
|
||||
case OP_BAND: return "BAND";
|
||||
case OP_BOR: return "BOR";
|
||||
case OP_BXOR: return "BXOR";
|
||||
case OP_BNOT: return "BNOT";
|
||||
case OP_SHL: return "SHL";
|
||||
case OP_SHR: return "SHR";
|
||||
case OP_ROTL: return "ROTL";
|
||||
case OP_ROTR: return "ROTR";
|
||||
case OP_JSON_PARSE: return "JSON_PARSE";
|
||||
case OP_JSON_STRINGIFY: return "JSON_STRINGIFY";
|
||||
case OP_JSON_FROM_FILE: return "JSON_FROM_FILE";
|
||||
case OP_JSON_TO_FILE: return "JSON_TO_FILE";
|
||||
case OP_CURL_GET: return "CURL_GET";
|
||||
case OP_CURL_POST: return "CURL_POST";
|
||||
case OP_CURL_DOWNLOAD: return "CURL_DOWNLOAD";
|
||||
case OP_SQLITE_OPEN: return "SQLITE_OPEN";
|
||||
case OP_SQLITE_CLOSE: return "SQLITE_CLOSE";
|
||||
case OP_SQLITE_EXEC: return "SQLITE_EXEC";
|
||||
case OP_SQLITE_QUERY: return "SQLITE_QUERY";
|
||||
case OP_LIBSQL_OPEN: return "LIBSQL_OPEN";
|
||||
case OP_LIBSQL_CLOSE: return "LIBSQL_CLOSE";
|
||||
case OP_LIBSQL_EXEC: return "LIBSQL_EXEC";
|
||||
case OP_LIBSQL_QUERY: return "LIBSQL_QUERY";
|
||||
case OP_PCSC_ESTABLISH: return "PCSC_ESTABLISH";
|
||||
case OP_PCSC_RELEASE: return "PCSC_RELEASE";
|
||||
case OP_PCSC_LIST_READERS: return "PCSC_LIST_READERS";
|
||||
case OP_PCSC_CONNECT: return "PCSC_CONNECT";
|
||||
case OP_PCSC_DISCONNECT: return "PCSC_DISCONNECT";
|
||||
case OP_PCSC_TRANSMIT: return "PCSC_TRANSMIT";
|
||||
case OP_PCRE2_TEST: return "PCRE2_TEST";
|
||||
case OP_PCRE2_MATCH: return "PCRE2_MATCH";
|
||||
case OP_PCRE2_FINDALL: return "PCRE2_FINDALL";
|
||||
case OP_OPENSSL_MD5: return "OPENSSL_MD5";
|
||||
case OP_OPENSSL_SHA256: return "OPENSSL_SHA256";
|
||||
case OP_OPENSSL_SHA512: return "OPENSSL_SHA512";
|
||||
case OP_OPENSSL_RIPEMD160: return "OPENSSL_RIPEMD160";
|
||||
case OP_LIBRESSL_MD5: return "LIBRESSL_MD5";
|
||||
case OP_LIBRESSL_SHA256: return "LIBRESSL_SHA256";
|
||||
case OP_LIBRESSL_SHA512: return "LIBRESSL_SHA512";
|
||||
case OP_LIBRESSL_RIPEMD160: return "LIBRESSL_RIPEMD160";
|
||||
case OP_INI_LOAD: return "INI_LOAD";
|
||||
case OP_INI_FREE: return "INI_FREE";
|
||||
case OP_INI_GET_STRING: return "INI_GET_STRING";
|
||||
case OP_INI_GET_INT: return "INI_GET_INT";
|
||||
case OP_INI_GET_DOUBLE: return "INI_GET_DOUBLE";
|
||||
case OP_INI_GET_BOOL: return "INI_GET_BOOL";
|
||||
case OP_INI_SET: return "INI_SET";
|
||||
case OP_INI_UNSET: return "INI_UNSET";
|
||||
case OP_INI_SAVE: return "INI_SAVE";
|
||||
case OP_XML_PARSE: return "XML_PARSE";
|
||||
case OP_XML_ROOT: return "XML_ROOT";
|
||||
case OP_XML_NAME: return "XML_NAME";
|
||||
case OP_XML_TEXT: return "XML_TEXT";
|
||||
case OP_TK_EVAL: return "TK_EVAL";
|
||||
case OP_TK_RESULT: return "TK_RESULT";
|
||||
case OP_TK_LOOP: return "TK_LOOP";
|
||||
case OP_TK_WM_TITLE: return "TK_WM_TITLE";
|
||||
case OP_TK_LABEL: return "TK_LABEL";
|
||||
case OP_TK_BUTTON: return "TK_BUTTON";
|
||||
case OP_TK_PACK: return "TK_PACK";
|
||||
case OP_FLOOR: return "FLOOR";
|
||||
case OP_CEIL: return "CEIL";
|
||||
case OP_TRUNC: return "TRUNC";
|
||||
case OP_ROUND: return "ROUND";
|
||||
case OP_SIN: return "SIN";
|
||||
case OP_COS: return "COS";
|
||||
case OP_TAN: return "TAN";
|
||||
case OP_EXP: return "EXP";
|
||||
case OP_LOG: return "LOG";
|
||||
case OP_LOG10: return "LOG10";
|
||||
case OP_SQRT: return "SQRT";
|
||||
case OP_GCD: return "GCD";
|
||||
case OP_LCM: return "LCM";
|
||||
case OP_ISQRT: return "ISQRT";
|
||||
case OP_SIGN: return "SIGN";
|
||||
case OP_FMIN: return "FMIN";
|
||||
case OP_FMAX: return "FMAX";
|
||||
case OP_RUST_HELLO: return "RUST_HELLO";
|
||||
case OP_RUST_HELLO_ARGS: return "RUST_HELLO_ARGS";
|
||||
case OP_RUST_HELLO_ARGS_RETURN: return "RUST_HELLO_ARGS_RETURN";
|
||||
case OP_RUST_GET_SP: return "RUST_GET_SP";
|
||||
case OP_RUST_SET_EXIT: return "RUST_SET_EXIT";
|
||||
default: return "???";
|
||||
}
|
||||
switch (op) {
|
||||
case OP_NOP:
|
||||
return "NOP";
|
||||
case OP_LOAD_CONST:
|
||||
return "LOAD_CONST";
|
||||
case OP_LOAD_LOCAL:
|
||||
return "LOAD_LOCAL";
|
||||
case OP_STORE_LOCAL:
|
||||
return "STORE_LOCAL";
|
||||
case OP_LOAD_GLOBAL:
|
||||
return "LOAD_GLOBAL";
|
||||
case OP_STORE_GLOBAL:
|
||||
return "STORE_GLOBAL";
|
||||
case OP_ADD:
|
||||
return "ADD";
|
||||
case OP_SUB:
|
||||
return "SUB";
|
||||
case OP_MUL:
|
||||
return "MUL";
|
||||
case OP_DIV:
|
||||
return "DIV";
|
||||
case OP_LT:
|
||||
return "LT";
|
||||
case OP_LTE:
|
||||
return "LTE";
|
||||
case OP_GT:
|
||||
return "GT";
|
||||
case OP_GTE:
|
||||
return "GTE";
|
||||
case OP_EQ:
|
||||
return "EQ";
|
||||
case OP_NEQ:
|
||||
return "NEQ";
|
||||
case OP_POP:
|
||||
return "POP";
|
||||
case OP_JUMP:
|
||||
return "JUMP";
|
||||
case OP_JUMP_IF_FALSE:
|
||||
return "JUMP_IF_FALSE";
|
||||
case OP_CALL:
|
||||
return "CALL";
|
||||
case OP_RETURN:
|
||||
return "RETURN";
|
||||
case OP_PRINT:
|
||||
return "PRINT";
|
||||
case OP_ECHO:
|
||||
return "ECHO";
|
||||
case OP_HALT:
|
||||
return "HALT";
|
||||
case OP_MOD:
|
||||
return "MOD";
|
||||
case OP_AND:
|
||||
return "AND";
|
||||
case OP_OR:
|
||||
return "OR";
|
||||
case OP_NOT:
|
||||
return "NOT";
|
||||
case OP_DUP:
|
||||
return "DUP";
|
||||
case OP_SWAP:
|
||||
return "SWAP";
|
||||
case OP_MAKE_ARRAY:
|
||||
return "MAKE_ARRAY";
|
||||
case OP_INDEX_GET:
|
||||
return "INDEX_GET";
|
||||
case OP_INDEX_SET:
|
||||
return "INDEX_SET";
|
||||
case OP_LEN:
|
||||
return "LEN";
|
||||
case OP_PUSH:
|
||||
return "ARR_PUSH";
|
||||
case OP_APOP:
|
||||
return "ARR_POP";
|
||||
case OP_SET:
|
||||
return "ARR_SET";
|
||||
case OP_INSERT:
|
||||
return "ARR_INSERT";
|
||||
case OP_REMOVE:
|
||||
return "ARR_REMOVE";
|
||||
case OP_SLICE:
|
||||
return "SLICE";
|
||||
case OP_TO_NUMBER:
|
||||
return "TO_NUMBER";
|
||||
case OP_TO_STRING:
|
||||
return "TO_STRING";
|
||||
case OP_TYPEOF:
|
||||
return "TYPEOF";
|
||||
case OP_CAST:
|
||||
return "CAST";
|
||||
case OP_SPLIT:
|
||||
return "SPLIT";
|
||||
case OP_JOIN:
|
||||
return "JOIN";
|
||||
case OP_SUBSTR:
|
||||
return "SUBSTR";
|
||||
case OP_FIND:
|
||||
return "FIND";
|
||||
case OP_REGEX_MATCH:
|
||||
return "REGEX_MATCH";
|
||||
case OP_REGEX_SEARCH:
|
||||
return "REGEX_SEARCH";
|
||||
case OP_REGEX_REPLACE:
|
||||
return "REGEX_REPLACE";
|
||||
case OP_CONTAINS:
|
||||
return "CONTAINS";
|
||||
case OP_INDEX_OF:
|
||||
return "INDEX_OF";
|
||||
case OP_CLEAR:
|
||||
return "CLEAR";
|
||||
case OP_ENUMERATE:
|
||||
return "ENUMERATE";
|
||||
case OP_ZIP:
|
||||
return "ZIP";
|
||||
case OP_MIN:
|
||||
return "MIN";
|
||||
case OP_MAX:
|
||||
return "MAX";
|
||||
case OP_CLAMP:
|
||||
return "CLAMP";
|
||||
case OP_ABS:
|
||||
return "ABS";
|
||||
case OP_POW:
|
||||
return "POW";
|
||||
case OP_RANDOM_SEED:
|
||||
return "RANDOM_SEED";
|
||||
case OP_RANDOM_INT:
|
||||
return "RANDOM_INT";
|
||||
case OP_MAKE_MAP:
|
||||
return "MAKE_MAP";
|
||||
case OP_KEYS:
|
||||
return "KEYS";
|
||||
case OP_VALUES:
|
||||
return "VALUES";
|
||||
case OP_HAS_KEY:
|
||||
return "HAS_KEY";
|
||||
case OP_READ_FILE:
|
||||
return "READ_FILE";
|
||||
case OP_WRITE_FILE:
|
||||
return "WRITE_FILE";
|
||||
case OP_ENV:
|
||||
return "ENV";
|
||||
case OP_INPUT_LINE:
|
||||
return "INPUT_LINE";
|
||||
case OP_PROC_RUN:
|
||||
return "PROC_RUN";
|
||||
case OP_PROC_SYSTEM:
|
||||
return "PROC_SYSTEM";
|
||||
case OP_TIME_NOW_MS:
|
||||
return "TIME_NOW_MS";
|
||||
case OP_CLOCK_MONO_MS:
|
||||
return "CLOCK_MONO_MS";
|
||||
case OP_DATE_FORMAT:
|
||||
return "DATE_FORMAT";
|
||||
case OP_ENV_ALL:
|
||||
return "ENV_ALL";
|
||||
case OP_FUN_VERSION:
|
||||
return "FUN_VERSION";
|
||||
case OP_THREAD_SPAWN:
|
||||
return "THREAD_SPAWN";
|
||||
case OP_THREAD_JOIN:
|
||||
return "THREAD_JOIN";
|
||||
case OP_SLEEP_MS:
|
||||
return "SLEEP_MS";
|
||||
case OP_RANDOM_NUMBER:
|
||||
return "RANDOM_NUMBER";
|
||||
case OP_BAND:
|
||||
return "BAND";
|
||||
case OP_BOR:
|
||||
return "BOR";
|
||||
case OP_BXOR:
|
||||
return "BXOR";
|
||||
case OP_BNOT:
|
||||
return "BNOT";
|
||||
case OP_SHL:
|
||||
return "SHL";
|
||||
case OP_SHR:
|
||||
return "SHR";
|
||||
case OP_ROTL:
|
||||
return "ROTL";
|
||||
case OP_ROTR:
|
||||
return "ROTR";
|
||||
case OP_JSON_PARSE:
|
||||
return "JSON_PARSE";
|
||||
case OP_JSON_STRINGIFY:
|
||||
return "JSON_STRINGIFY";
|
||||
case OP_JSON_FROM_FILE:
|
||||
return "JSON_FROM_FILE";
|
||||
case OP_JSON_TO_FILE:
|
||||
return "JSON_TO_FILE";
|
||||
case OP_CURL_GET:
|
||||
return "CURL_GET";
|
||||
case OP_CURL_POST:
|
||||
return "CURL_POST";
|
||||
case OP_CURL_DOWNLOAD:
|
||||
return "CURL_DOWNLOAD";
|
||||
case OP_SQLITE_OPEN:
|
||||
return "SQLITE_OPEN";
|
||||
case OP_SQLITE_CLOSE:
|
||||
return "SQLITE_CLOSE";
|
||||
case OP_SQLITE_EXEC:
|
||||
return "SQLITE_EXEC";
|
||||
case OP_SQLITE_QUERY:
|
||||
return "SQLITE_QUERY";
|
||||
case OP_LIBSQL_OPEN:
|
||||
return "LIBSQL_OPEN";
|
||||
case OP_LIBSQL_CLOSE:
|
||||
return "LIBSQL_CLOSE";
|
||||
case OP_LIBSQL_EXEC:
|
||||
return "LIBSQL_EXEC";
|
||||
case OP_LIBSQL_QUERY:
|
||||
return "LIBSQL_QUERY";
|
||||
case OP_PCSC_ESTABLISH:
|
||||
return "PCSC_ESTABLISH";
|
||||
case OP_PCSC_RELEASE:
|
||||
return "PCSC_RELEASE";
|
||||
case OP_PCSC_LIST_READERS:
|
||||
return "PCSC_LIST_READERS";
|
||||
case OP_PCSC_CONNECT:
|
||||
return "PCSC_CONNECT";
|
||||
case OP_PCSC_DISCONNECT:
|
||||
return "PCSC_DISCONNECT";
|
||||
case OP_PCSC_TRANSMIT:
|
||||
return "PCSC_TRANSMIT";
|
||||
case OP_PCRE2_TEST:
|
||||
return "PCRE2_TEST";
|
||||
case OP_PCRE2_MATCH:
|
||||
return "PCRE2_MATCH";
|
||||
case OP_PCRE2_FINDALL:
|
||||
return "PCRE2_FINDALL";
|
||||
case OP_OPENSSL_MD5:
|
||||
return "OPENSSL_MD5";
|
||||
case OP_OPENSSL_SHA256:
|
||||
return "OPENSSL_SHA256";
|
||||
case OP_OPENSSL_SHA512:
|
||||
return "OPENSSL_SHA512";
|
||||
case OP_OPENSSL_RIPEMD160:
|
||||
return "OPENSSL_RIPEMD160";
|
||||
case OP_LIBRESSL_MD5:
|
||||
return "LIBRESSL_MD5";
|
||||
case OP_LIBRESSL_SHA256:
|
||||
return "LIBRESSL_SHA256";
|
||||
case OP_LIBRESSL_SHA512:
|
||||
return "LIBRESSL_SHA512";
|
||||
case OP_LIBRESSL_RIPEMD160:
|
||||
return "LIBRESSL_RIPEMD160";
|
||||
case OP_INI_LOAD:
|
||||
return "INI_LOAD";
|
||||
case OP_INI_FREE:
|
||||
return "INI_FREE";
|
||||
case OP_INI_GET_STRING:
|
||||
return "INI_GET_STRING";
|
||||
case OP_INI_GET_INT:
|
||||
return "INI_GET_INT";
|
||||
case OP_INI_GET_DOUBLE:
|
||||
return "INI_GET_DOUBLE";
|
||||
case OP_INI_GET_BOOL:
|
||||
return "INI_GET_BOOL";
|
||||
case OP_INI_SET:
|
||||
return "INI_SET";
|
||||
case OP_INI_UNSET:
|
||||
return "INI_UNSET";
|
||||
case OP_INI_SAVE:
|
||||
return "INI_SAVE";
|
||||
case OP_XML_PARSE:
|
||||
return "XML_PARSE";
|
||||
case OP_XML_ROOT:
|
||||
return "XML_ROOT";
|
||||
case OP_XML_NAME:
|
||||
return "XML_NAME";
|
||||
case OP_XML_TEXT:
|
||||
return "XML_TEXT";
|
||||
case OP_TK_EVAL:
|
||||
return "TK_EVAL";
|
||||
case OP_TK_RESULT:
|
||||
return "TK_RESULT";
|
||||
case OP_TK_LOOP:
|
||||
return "TK_LOOP";
|
||||
case OP_TK_WM_TITLE:
|
||||
return "TK_WM_TITLE";
|
||||
case OP_TK_LABEL:
|
||||
return "TK_LABEL";
|
||||
case OP_TK_BUTTON:
|
||||
return "TK_BUTTON";
|
||||
case OP_TK_PACK:
|
||||
return "TK_PACK";
|
||||
case OP_FLOOR:
|
||||
return "FLOOR";
|
||||
case OP_CEIL:
|
||||
return "CEIL";
|
||||
case OP_TRUNC:
|
||||
return "TRUNC";
|
||||
case OP_ROUND:
|
||||
return "ROUND";
|
||||
case OP_SIN:
|
||||
return "SIN";
|
||||
case OP_COS:
|
||||
return "COS";
|
||||
case OP_TAN:
|
||||
return "TAN";
|
||||
case OP_EXP:
|
||||
return "EXP";
|
||||
case OP_LOG:
|
||||
return "LOG";
|
||||
case OP_LOG10:
|
||||
return "LOG10";
|
||||
case OP_SQRT:
|
||||
return "SQRT";
|
||||
case OP_GCD:
|
||||
return "GCD";
|
||||
case OP_LCM:
|
||||
return "LCM";
|
||||
case OP_ISQRT:
|
||||
return "ISQRT";
|
||||
case OP_SIGN:
|
||||
return "SIGN";
|
||||
case OP_FMIN:
|
||||
return "FMIN";
|
||||
case OP_FMAX:
|
||||
return "FMAX";
|
||||
case OP_RUST_HELLO:
|
||||
return "RUST_HELLO";
|
||||
case OP_RUST_HELLO_ARGS:
|
||||
return "RUST_HELLO_ARGS";
|
||||
case OP_RUST_HELLO_ARGS_RETURN:
|
||||
return "RUST_HELLO_ARGS_RETURN";
|
||||
case OP_RUST_GET_SP:
|
||||
return "RUST_GET_SP";
|
||||
case OP_RUST_SET_EXIT:
|
||||
return "RUST_SET_EXIT";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
void bytecode_dump(const Bytecode *bc) {
|
||||
if (!bc) {
|
||||
printf("<null bytecode>\n");
|
||||
return;
|
||||
}
|
||||
printf("Constants (%d):\n", bc->const_count);
|
||||
for (int i = 0; i < bc->const_count; ++i) {
|
||||
printf(" [%d] ", i);
|
||||
print_value(&bc->constants[i]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("Instructions (%d):\n", bc->instr_count);
|
||||
for (int i = 0; i < bc->instr_count; ++i) {
|
||||
const Instruction *ins = &bc->instructions[i];
|
||||
printf(" %3d: %-15s %d\n", i, opcode_name(ins->op), ins->operand);
|
||||
}
|
||||
if (!bc) {
|
||||
printf("<null bytecode>\n");
|
||||
return;
|
||||
}
|
||||
printf("Constants (%d):\n", bc->const_count);
|
||||
for (int i = 0; i < bc->const_count; ++i) {
|
||||
printf(" [%d] ", i);
|
||||
print_value(&bc->constants[i]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("Instructions (%d):\n", bc->instr_count);
|
||||
for (int i = 0; i < bc->instr_count; ++i) {
|
||||
const Instruction *ins = &bc->instructions[i];
|
||||
printf(" %3d: %-15s %d\n", i, opcode_name(ins->op), ins->operand);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
480
src/bytecode.h
480
src/bytecode.h
|
|
@ -10,304 +10,304 @@
|
|||
#ifndef FUN_BYTECODE_H
|
||||
#define FUN_BYTECODE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "value.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// VM opcodes
|
||||
typedef enum {
|
||||
OP_NOP,
|
||||
OP_LOAD_CONST, // operand = constant index
|
||||
OP_LOAD_LOCAL, // operand = local slot index
|
||||
OP_STORE_LOCAL, // operand = local slot index
|
||||
OP_NOP,
|
||||
OP_LOAD_CONST, // operand = constant index
|
||||
OP_LOAD_LOCAL, // operand = local slot index
|
||||
OP_STORE_LOCAL, // operand = local slot index
|
||||
|
||||
OP_LOAD_GLOBAL, //
|
||||
OP_STORE_GLOBAL, //
|
||||
OP_LOAD_GLOBAL, //
|
||||
OP_STORE_GLOBAL, //
|
||||
|
||||
OP_ADD, //
|
||||
OP_SUB, //
|
||||
OP_MUL, //
|
||||
OP_DIV, //
|
||||
OP_ADD, //
|
||||
OP_SUB, //
|
||||
OP_MUL, //
|
||||
OP_DIV, //
|
||||
|
||||
OP_LT, // a < b -> push 1/0
|
||||
OP_LTE, // a <= b -> push 1/0
|
||||
OP_GT, // a > b -> push 1/0
|
||||
OP_GTE, // a >= b -> push 1/0
|
||||
OP_EQ, // a == b -> push 1/0
|
||||
OP_NEQ, // a != b -> push 1/0
|
||||
OP_LT, // a < b -> push 1/0
|
||||
OP_LTE, // a <= b -> push 1/0
|
||||
OP_GT, // a > b -> push 1/0
|
||||
OP_GTE, // a >= b -> push 1/0
|
||||
OP_EQ, // a == b -> push 1/0
|
||||
OP_NEQ, // a != b -> push 1/0
|
||||
|
||||
OP_POP, // discard top of stack
|
||||
OP_JUMP, // unconditional jump
|
||||
OP_JUMP_IF_FALSE, // jump if top of stack is false (0)
|
||||
OP_POP, // discard top of stack
|
||||
OP_JUMP, // unconditional jump
|
||||
OP_JUMP_IF_FALSE, // jump if top of stack is false (0)
|
||||
|
||||
OP_CALL, // operand = arg count; pops fn + args and enters fn
|
||||
OP_RETURN, // pop optional return value and return to caller
|
||||
OP_CALL, // operand = arg count; pops fn + args and enters fn
|
||||
OP_RETURN, // pop optional return value and return to caller
|
||||
|
||||
OP_PRINT,
|
||||
OP_ECHO, // like print but does not append a newline; prints immediately
|
||||
OP_HALT,
|
||||
OP_PRINT,
|
||||
OP_ECHO, // like print but does not append a newline; prints immediately
|
||||
OP_HALT,
|
||||
|
||||
OP_LINE, // operand = source line number (debug marker)
|
||||
OP_LINE, // operand = source line number (debug marker)
|
||||
|
||||
// add after existing opcodes
|
||||
OP_MOD, // a % b
|
||||
OP_AND, // logical AND
|
||||
OP_OR, // logical OR
|
||||
OP_NOT, // logical NOT
|
||||
// add after existing opcodes
|
||||
OP_MOD, // a % b
|
||||
OP_AND, // logical AND
|
||||
OP_OR, // logical OR
|
||||
OP_NOT, // logical NOT
|
||||
|
||||
OP_DUP, // duplicate top of stack
|
||||
OP_SWAP, // swap top two stack values
|
||||
OP_DUP, // duplicate top of stack
|
||||
OP_SWAP, // swap top two stack values
|
||||
|
||||
// arrays
|
||||
OP_MAKE_ARRAY, // operand = element count; pops N values, pushes array
|
||||
OP_INDEX_GET, // pops index, array; pushes element copy
|
||||
OP_INDEX_SET, // pops value, index, array; sets in place
|
||||
// arrays
|
||||
OP_MAKE_ARRAY, // operand = element count; pops N values, pushes array
|
||||
OP_INDEX_GET, // pops index, array; pushes element copy
|
||||
OP_INDEX_SET, // pops value, index, array; sets in place
|
||||
|
||||
// array and builtin helpers
|
||||
OP_LEN, // pops array or string; pushes length
|
||||
OP_PUSH, // pops value, array; pushes new length
|
||||
OP_APOP, // pops array; pushes removed element
|
||||
OP_SET, // pops value, index, array; pushes value
|
||||
OP_INSERT, // pops value, index, array; pushes new length
|
||||
OP_REMOVE, // pops index, array; pushes removed element
|
||||
OP_SLICE, // pops end, start, array; pushes new array
|
||||
// array and builtin helpers
|
||||
OP_LEN, // pops array or string; pushes length
|
||||
OP_PUSH, // pops value, array; pushes new length
|
||||
OP_APOP, // pops array; pushes removed element
|
||||
OP_SET, // pops value, index, array; pushes value
|
||||
OP_INSERT, // pops value, index, array; pushes new length
|
||||
OP_REMOVE, // pops index, array; pushes removed element
|
||||
OP_SLICE, // pops end, start, array; pushes new array
|
||||
|
||||
// conversions
|
||||
OP_TO_NUMBER, // pops any; pushes int (parse strings)
|
||||
OP_TO_STRING, // pops any; pushes string
|
||||
OP_CAST, // pops typeName, value; pushes casted value (see vm/cast.c)
|
||||
OP_TYPEOF, // pops any; pushes string name of type
|
||||
OP_UCLAMP, // pops number; pushes number masked to N bits (operand = bits)
|
||||
OP_SCLAMP, // pops number; pushes number clamped to signed N-bit range (operand = bits)
|
||||
// conversions
|
||||
OP_TO_NUMBER, // pops any; pushes int (parse strings)
|
||||
OP_TO_STRING, // pops any; pushes string
|
||||
OP_CAST, // pops typeName, value; pushes casted value (see vm/cast.c)
|
||||
OP_TYPEOF, // pops any; pushes string name of type
|
||||
OP_UCLAMP, // pops number; pushes number masked to N bits (operand = bits)
|
||||
OP_SCLAMP, // pops number; pushes number clamped to signed N-bit range (operand = bits)
|
||||
|
||||
// string ops
|
||||
OP_SPLIT, // pops sep, string; pushes array of strings
|
||||
OP_JOIN, // pops sep, array; pushes string
|
||||
OP_SUBSTR, // pops len, start, string; pushes string
|
||||
OP_FIND, // pops needle, haystack; pushes int index or -1
|
||||
// string ops
|
||||
OP_SPLIT, // pops sep, string; pushes array of strings
|
||||
OP_JOIN, // pops sep, array; pushes string
|
||||
OP_SUBSTR, // pops len, start, string; pushes string
|
||||
OP_FIND, // pops needle, haystack; pushes int index or -1
|
||||
|
||||
// regex ops (POSIX)
|
||||
OP_REGEX_MATCH, // pops pattern, string; pushes 1/0 for full match
|
||||
OP_REGEX_SEARCH, // pops pattern, string; pushes map {"match":str, "start":int, "end":int, "groups":array}
|
||||
OP_REGEX_REPLACE, // pops repl, pattern, string; pushes string with global replacements
|
||||
// regex ops (POSIX)
|
||||
OP_REGEX_MATCH, // pops pattern, string; pushes 1/0 for full match
|
||||
OP_REGEX_SEARCH, // pops pattern, string; pushes map {"match":str, "start":int, "end":int, "groups":array}
|
||||
OP_REGEX_REPLACE, // pops repl, pattern, string; pushes string with global replacements
|
||||
|
||||
// array utils
|
||||
OP_CONTAINS, // pops value, array; pushes 1/0
|
||||
OP_INDEX_OF, // pops value, array; pushes index or -1
|
||||
OP_CLEAR, // pops array; clears it; pushes nothing (we'll push 0)
|
||||
// array utils
|
||||
OP_CONTAINS, // pops value, array; pushes 1/0
|
||||
OP_INDEX_OF, // pops value, array; pushes index or -1
|
||||
OP_CLEAR, // pops array; clears it; pushes nothing (we'll push 0)
|
||||
|
||||
// iteration helpers
|
||||
OP_ENUMERATE, // pops array; pushes array of [index, value]
|
||||
OP_ZIP, // pops b, a; pushes array of [a[i], b[i]]
|
||||
// iteration helpers
|
||||
OP_ENUMERATE, // pops array; pushes array of [index, value]
|
||||
OP_ZIP, // pops b, a; pushes array of [a[i], b[i]]
|
||||
|
||||
// math
|
||||
OP_MIN, // pops b, a; pushes min(a,b)
|
||||
OP_MAX, // pops b, a; pushes max(a,b)
|
||||
OP_CLAMP, // pops hi, lo, x; pushes clamped
|
||||
OP_ABS, // pops x; pushes |x|
|
||||
OP_POW, // pops b, a; pushes a^b
|
||||
OP_RANDOM_SEED, // pops seed; sets RNG seed; pushes nothing (we'll push 0)
|
||||
OP_RANDOM_INT, // pops hi, lo; pushes random int in [lo, hi)
|
||||
// math
|
||||
OP_MIN, // pops b, a; pushes min(a,b)
|
||||
OP_MAX, // pops b, a; pushes max(a,b)
|
||||
OP_CLAMP, // pops hi, lo, x; pushes clamped
|
||||
OP_ABS, // pops x; pushes |x|
|
||||
OP_POW, // pops b, a; pushes a^b
|
||||
OP_RANDOM_SEED, // pops seed; sets RNG seed; pushes nothing (we'll push 0)
|
||||
OP_RANDOM_INT, // pops hi, lo; pushes random int in [lo, hi)
|
||||
|
||||
// maps
|
||||
OP_MAKE_MAP, // operand = pair count; pops 2*n (key,value)..., pushes map
|
||||
OP_KEYS, // pops map; pushes array of keys
|
||||
OP_VALUES, // pops map; pushes array of values
|
||||
OP_HAS_KEY, // pops key, map; pushes 1/0
|
||||
// maps
|
||||
OP_MAKE_MAP, // operand = pair count; pops 2*n (key,value)..., pushes map
|
||||
OP_KEYS, // pops map; pushes array of keys
|
||||
OP_VALUES, // pops map; pushes array of values
|
||||
OP_HAS_KEY, // pops key, map; pushes 1/0
|
||||
|
||||
// file I/O
|
||||
OP_READ_FILE, // pops path string; pushes content string (or "")
|
||||
OP_WRITE_FILE, // pops data string, path string; pushes 1/0
|
||||
// file I/O
|
||||
OP_READ_FILE, // pops path string; pushes content string (or "")
|
||||
OP_WRITE_FILE, // pops data string, path string; pushes 1/0
|
||||
|
||||
// OS
|
||||
OP_ENV, // pops name string; pushes value string (or "")
|
||||
OP_INPUT_LINE, // operand: 0=no prompt; 1=has prompt. Pops [prompt?]; pushes input string (no trailing newline)
|
||||
OP_PROC_RUN, // pops command string; pushes map {"out": string, "code": int}
|
||||
OP_PROC_SYSTEM, // pops command string; pushes exit code number
|
||||
OP_TIME_NOW_MS, // pushes current wall-clock time in milliseconds since Unix epoch
|
||||
OP_CLOCK_MONO_MS, // pushes monotonic clock in milliseconds (not wall time)
|
||||
OP_DATE_FORMAT, // pops fmt string, ms epoch (int); pushes formatted date string using strftime
|
||||
OP_ENV_ALL, // pushes map of all environment variables
|
||||
OP_FUN_VERSION, // pushes version string
|
||||
// OS
|
||||
OP_ENV, // pops name string; pushes value string (or "")
|
||||
OP_INPUT_LINE, // operand: 0=no prompt; 1=has prompt. Pops [prompt?]; pushes input string (no trailing newline)
|
||||
OP_PROC_RUN, // pops command string; pushes map {"out": string, "code": int}
|
||||
OP_PROC_SYSTEM, // pops command string; pushes exit code number
|
||||
OP_TIME_NOW_MS, // pushes current wall-clock time in milliseconds since Unix epoch
|
||||
OP_CLOCK_MONO_MS, // pushes monotonic clock in milliseconds (not wall time)
|
||||
OP_DATE_FORMAT, // pops fmt string, ms epoch (int); pushes formatted date string using strftime
|
||||
OP_ENV_ALL, // pushes map of all environment variables
|
||||
OP_FUN_VERSION, // pushes version string
|
||||
|
||||
// Threads
|
||||
OP_THREAD_SPAWN, // operand: 0=no args, 1=has args; pops [args?], fn; pushes thread id (int>0)
|
||||
OP_THREAD_JOIN, // pops thread id; waits; pushes result value (or Nil)
|
||||
OP_SLEEP_MS, // pops milliseconds; sleeps; pushes Nil (for statement POP safety)
|
||||
OP_RANDOM_NUMBER, // pops length; pushes hex string of that length from OS RNG (hex-encoded)
|
||||
// Threads
|
||||
OP_THREAD_SPAWN, // operand: 0=no args, 1=has args; pops [args?], fn; pushes thread id (int>0)
|
||||
OP_THREAD_JOIN, // pops thread id; waits; pushes result value (or Nil)
|
||||
OP_SLEEP_MS, // pops milliseconds; sleeps; pushes Nil (for statement POP safety)
|
||||
OP_RANDOM_NUMBER, // pops length; pushes hex string of that length from OS RNG (hex-encoded)
|
||||
|
||||
// Bitwise (32-bit) and shifts/rotates
|
||||
OP_BAND, // pops b, a; pushes (uint32_t)(a & b)
|
||||
OP_BOR, // pops b, a; pushes (uint32_t)(a | b)
|
||||
OP_BXOR, // pops b, a; pushes (uint32_t)(a ^ b)
|
||||
OP_BNOT, // pops a; pushes (uint32_t)(~a)
|
||||
OP_SHL, // pops s, a; pushes (uint32_t)(a << (s&31))
|
||||
OP_SHR, // pops s, a; pushes (uint32_t)(a >> (s&31)) logical
|
||||
OP_ROTL, // pops s, a; pushes rotl32(a, s)
|
||||
OP_ROTR, // pops s, a; pushes rotr32(a, s)
|
||||
// Bitwise (32-bit) and shifts/rotates
|
||||
OP_BAND, // pops b, a; pushes (uint32_t)(a & b)
|
||||
OP_BOR, // pops b, a; pushes (uint32_t)(a | b)
|
||||
OP_BXOR, // pops b, a; pushes (uint32_t)(a ^ b)
|
||||
OP_BNOT, // pops a; pushes (uint32_t)(~a)
|
||||
OP_SHL, // pops s, a; pushes (uint32_t)(a << (s&31))
|
||||
OP_SHR, // pops s, a; pushes (uint32_t)(a >> (s&31)) logical
|
||||
OP_ROTL, // pops s, a; pushes rotl32(a, s)
|
||||
OP_ROTR, // pops s, a; pushes rotr32(a, s)
|
||||
|
||||
// JSON (json-c)
|
||||
OP_JSON_PARSE, // pops text string; pushes value (or Nil on error)
|
||||
OP_JSON_STRINGIFY, // pops pretty(bool), value; pushes string
|
||||
OP_JSON_FROM_FILE, // pops path string; pushes value (or Nil)
|
||||
OP_JSON_TO_FILE, // pops pretty(bool), value, path; pushes 1/0
|
||||
// JSON (json-c)
|
||||
OP_JSON_PARSE, // pops text string; pushes value (or Nil on error)
|
||||
OP_JSON_STRINGIFY, // pops pretty(bool), value; pushes string
|
||||
OP_JSON_FROM_FILE, // pops path string; pushes value (or Nil)
|
||||
OP_JSON_TO_FILE, // pops pretty(bool), value, path; pushes 1/0
|
||||
|
||||
// CURL (libcurl)
|
||||
OP_CURL_GET, // pops [headers map?], url; pushes response string (or "")
|
||||
OP_CURL_POST, // pops [headers map?], body string, url; pushes response string (or "")
|
||||
OP_CURL_DOWNLOAD, // pops [headers map?], path, url; pushes 1/0
|
||||
// CURL (libcurl)
|
||||
OP_CURL_GET, // pops [headers map?], url; pushes response string (or "")
|
||||
OP_CURL_POST, // pops [headers map?], body string, url; pushes response string (or "")
|
||||
OP_CURL_DOWNLOAD, // pops [headers map?], path, url; pushes 1/0
|
||||
|
||||
// SQLite (optional)
|
||||
OP_SQLITE_OPEN, // pops path; pushes handle (>0) or 0
|
||||
OP_SQLITE_CLOSE, // pops handle; pushes Nil
|
||||
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
|
||||
OP_SQLITE_QUERY, // pops sql, handle; pushes array<map>
|
||||
// SQLite (optional)
|
||||
OP_SQLITE_OPEN, // pops path; pushes handle (>0) or 0
|
||||
OP_SQLITE_CLOSE, // pops handle; pushes Nil
|
||||
OP_SQLITE_EXEC, // pops sql, handle; pushes sqlite rc (0=OK)
|
||||
OP_SQLITE_QUERY, // pops sql, handle; pushes array<map>
|
||||
|
||||
// libsql (optional, independent)
|
||||
OP_LIBSQL_OPEN, // pops url/path; pushes handle (>0) or 0
|
||||
OP_LIBSQL_CLOSE, // pops handle; pushes Nil
|
||||
OP_LIBSQL_EXEC, // pops sql, handle; pushes rc (0=OK)
|
||||
OP_LIBSQL_QUERY, // pops sql, handle; pushes array<map>
|
||||
// libsql (optional, independent)
|
||||
OP_LIBSQL_OPEN, // pops url/path; pushes handle (>0) or 0
|
||||
OP_LIBSQL_CLOSE, // pops handle; pushes Nil
|
||||
OP_LIBSQL_EXEC, // pops sql, handle; pushes rc (0=OK)
|
||||
OP_LIBSQL_QUERY, // pops sql, handle; pushes array<map>
|
||||
|
||||
// PCSC (smart card) opcodes
|
||||
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
|
||||
OP_PCSC_RELEASE, // pops ctx id; returns 1/0
|
||||
OP_PCSC_LIST_READERS, // pops ctx id; returns array of reader names (possibly empty)
|
||||
OP_PCSC_CONNECT, // pops reader, ctx id; returns handle id (>0) or 0
|
||||
OP_PCSC_DISCONNECT, // pops handle id; returns 1/0
|
||||
OP_PCSC_TRANSMIT, // pops apdu array, handle id; returns map {"data":[],"sw1":n,"sw2":n,"code":n}
|
||||
// PCSC (smart card) opcodes
|
||||
OP_PCSC_ESTABLISH, // returns context id (>0) or 0
|
||||
OP_PCSC_RELEASE, // pops ctx id; returns 1/0
|
||||
OP_PCSC_LIST_READERS, // pops ctx id; returns array of reader names (possibly empty)
|
||||
OP_PCSC_CONNECT, // pops reader, ctx id; returns handle id (>0) or 0
|
||||
OP_PCSC_DISCONNECT, // pops handle id; returns 1/0
|
||||
OP_PCSC_TRANSMIT, // pops apdu array, handle id; returns map {"data":[],"sw1":n,"sw2":n,"code":n}
|
||||
|
||||
// PCRE2 regex ops (optional)
|
||||
OP_PCRE2_TEST, // pops flags, text, pattern; pushes 1/0
|
||||
OP_PCRE2_MATCH, // pops flags, text, pattern; pushes match map or Nil
|
||||
OP_PCRE2_FINDALL, // pops flags, text, pattern; pushes array of match maps
|
||||
// PCRE2 regex ops (optional)
|
||||
OP_PCRE2_TEST, // pops flags, text, pattern; pushes 1/0
|
||||
OP_PCRE2_MATCH, // pops flags, text, pattern; pushes match map or Nil
|
||||
OP_PCRE2_FINDALL, // pops flags, text, pattern; pushes array of match maps
|
||||
|
||||
// OpenSSL (optional)
|
||||
OP_OPENSSL_MD5, // pops data string; pushes md5 hex string
|
||||
OP_OPENSSL_SHA256, // pops data string; pushes sha256 hex string
|
||||
OP_OPENSSL_SHA512, // pops data string; pushes sha512 hex string
|
||||
OP_OPENSSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
|
||||
// OpenSSL (optional)
|
||||
OP_OPENSSL_MD5, // pops data string; pushes md5 hex string
|
||||
OP_OPENSSL_SHA256, // pops data string; pushes sha256 hex string
|
||||
OP_OPENSSL_SHA512, // pops data string; pushes sha512 hex string
|
||||
OP_OPENSSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
|
||||
|
||||
// LibreSSL (optional; same API as OpenSSL but different toggle)
|
||||
OP_LIBRESSL_MD5, // pops data string; pushes md5 hex string
|
||||
OP_LIBRESSL_SHA256, // pops data string; pushes sha256 hex string
|
||||
OP_LIBRESSL_SHA512, // pops data string; pushes sha512 hex string
|
||||
OP_LIBRESSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
|
||||
// LibreSSL (optional; same API as OpenSSL but different toggle)
|
||||
OP_LIBRESSL_MD5, // pops data string; pushes md5 hex string
|
||||
OP_LIBRESSL_SHA256, // pops data string; pushes sha256 hex string
|
||||
OP_LIBRESSL_SHA512, // pops data string; pushes sha512 hex string
|
||||
OP_LIBRESSL_RIPEMD160, // pops data string; pushes ripemd160 hex string
|
||||
|
||||
// INI (iniparser 4.2.6) optional
|
||||
OP_INI_LOAD, // pops path; pushes handle (>0) or 0
|
||||
OP_INI_FREE, // pops handle; pushes 1/0
|
||||
OP_INI_GET_STRING, // pops def, key, section, handle; pushes string
|
||||
OP_INI_GET_INT, // pops def, key, section, handle; pushes int
|
||||
OP_INI_GET_DOUBLE, // pops def, key, section, handle; pushes float
|
||||
OP_INI_GET_BOOL, // pops def, key, section, handle; pushes int (0/1)
|
||||
OP_INI_SET, // pops value, key, section, handle; pushes 1/0
|
||||
OP_INI_UNSET, // pops key, section, handle; pushes 1/0
|
||||
OP_INI_SAVE, // pops path, handle; pushes 1/0
|
||||
// INI (iniparser 4.2.6) optional
|
||||
OP_INI_LOAD, // pops path; pushes handle (>0) or 0
|
||||
OP_INI_FREE, // pops handle; pushes 1/0
|
||||
OP_INI_GET_STRING, // pops def, key, section, handle; pushes string
|
||||
OP_INI_GET_INT, // pops def, key, section, handle; pushes int
|
||||
OP_INI_GET_DOUBLE, // pops def, key, section, handle; pushes float
|
||||
OP_INI_GET_BOOL, // pops def, key, section, handle; pushes int (0/1)
|
||||
OP_INI_SET, // pops value, key, section, handle; pushes 1/0
|
||||
OP_INI_UNSET, // pops key, section, handle; pushes 1/0
|
||||
OP_INI_SAVE, // pops path, handle; pushes 1/0
|
||||
|
||||
// XML (libxml2) optional minimal API
|
||||
OP_XML_PARSE, // pops text string; pushes doc handle (>0) or 0
|
||||
OP_XML_ROOT, // pops doc handle; pushes node handle (>0) or 0
|
||||
OP_XML_NAME, // pops node handle; pushes string (node name)
|
||||
OP_XML_TEXT, // pops node handle; pushes string (node text)
|
||||
// XML (libxml2) optional minimal API
|
||||
OP_XML_PARSE, // pops text string; pushes doc handle (>0) or 0
|
||||
OP_XML_ROOT, // pops doc handle; pushes node handle (>0) or 0
|
||||
OP_XML_NAME, // pops node handle; pushes string (node name)
|
||||
OP_XML_TEXT, // pops node handle; pushes string (node text)
|
||||
|
||||
// Sockets (UNIX platforms)
|
||||
OP_SOCK_TCP_LISTEN, // pops backlog, port; returns listen fd (>0) or 0
|
||||
OP_SOCK_TCP_ACCEPT, // pops listen fd; returns client fd (>0) or 0
|
||||
OP_SOCK_TCP_CONNECT, // pops port, host; returns fd (>0) or 0
|
||||
OP_SOCK_SEND, // pops data string, fd; returns bytes sent (>=0) or -1
|
||||
OP_SOCK_RECV, // pops maxlen, fd; returns data string ("" on EOF/error)
|
||||
OP_SOCK_CLOSE, // pops fd; returns 1/0
|
||||
OP_SOCK_UNIX_LISTEN, // pops backlog, path; returns listen fd (>0) or 0
|
||||
OP_SOCK_UNIX_CONNECT, // pops path; returns fd (>0) or 0
|
||||
// Sockets (UNIX platforms)
|
||||
OP_SOCK_TCP_LISTEN, // pops backlog, port; returns listen fd (>0) or 0
|
||||
OP_SOCK_TCP_ACCEPT, // pops listen fd; returns client fd (>0) or 0
|
||||
OP_SOCK_TCP_CONNECT, // pops port, host; returns fd (>0) or 0
|
||||
OP_SOCK_SEND, // pops data string, fd; returns bytes sent (>=0) or -1
|
||||
OP_SOCK_RECV, // pops maxlen, fd; returns data string ("" on EOF/error)
|
||||
OP_SOCK_CLOSE, // pops fd; returns 1/0
|
||||
OP_SOCK_UNIX_LISTEN, // pops backlog, path; returns listen fd (>0) or 0
|
||||
OP_SOCK_UNIX_CONNECT, // pops path; returns fd (>0) or 0
|
||||
|
||||
// process control
|
||||
OP_EXIT, // pops code (or uses operand) and terminates script with exit code
|
||||
// process control
|
||||
OP_EXIT, // pops code (or uses operand) and terminates script with exit code
|
||||
|
||||
// OS additions
|
||||
OP_OS_LIST_DIR, // pops path string; pushes array of strings
|
||||
// OS additions
|
||||
OP_OS_LIST_DIR, // pops path string; pushes array of strings
|
||||
|
||||
// Tk additions
|
||||
OP_TK_BIND, // pops command, event, id; binds event to command
|
||||
// Tk additions
|
||||
OP_TK_BIND, // pops command, event, id; binds event to command
|
||||
|
||||
// Serial communication (termios)
|
||||
OP_SERIAL_OPEN, // pops baud_rate (int), path (string); returns fd (int) or 0
|
||||
OP_SERIAL_CONFIG, // pops flow_control, stop_bits, parity, data_bits, fd; returns 1/0
|
||||
OP_SERIAL_SEND, // pops data (string), fd; returns bytes sent (int)
|
||||
OP_SERIAL_RECV, // pops maxlen (int), fd; returns data (string)
|
||||
OP_SERIAL_CLOSE, // pops fd; returns 1/0
|
||||
// Serial communication (termios)
|
||||
OP_SERIAL_OPEN, // pops baud_rate (int), path (string); returns fd (int) or 0
|
||||
OP_SERIAL_CONFIG, // pops flow_control, stop_bits, parity, data_bits, fd; returns 1/0
|
||||
OP_SERIAL_SEND, // pops data (string), fd; returns bytes sent (int)
|
||||
OP_SERIAL_RECV, // pops maxlen (int), fd; returns data (string)
|
||||
OP_SERIAL_CLOSE, // pops fd; returns 1/0
|
||||
|
||||
// Tk (Tcl/Tk) optional minimal API
|
||||
OP_TK_EVAL, // pops script string; pushes int rc (0 = OK)
|
||||
OP_TK_RESULT, // pushes string: last Tcl result
|
||||
OP_TK_LOOP, // enters Tk event loop; pushes Nil when done
|
||||
OP_TK_WM_TITLE, // pops title string; sets window title; pushes rc
|
||||
OP_TK_LABEL, // pops text, id; creates/updates label .id; pushes rc
|
||||
OP_TK_BUTTON, // pops text, id; creates/updates button .id; pushes rc
|
||||
OP_TK_PACK, // pops id; packs .id; pushes rc
|
||||
// Tk (Tcl/Tk) optional minimal API
|
||||
OP_TK_EVAL, // pops script string; pushes int rc (0 = OK)
|
||||
OP_TK_RESULT, // pushes string: last Tcl result
|
||||
OP_TK_LOOP, // enters Tk event loop; pushes Nil when done
|
||||
OP_TK_WM_TITLE, // pops title string; sets window title; pushes rc
|
||||
OP_TK_LABEL, // pops text, id; creates/updates label .id; pushes rc
|
||||
OP_TK_BUTTON, // pops text, id; creates/updates button .id; pushes rc
|
||||
OP_TK_PACK, // pops id; packs .id; pushes rc
|
||||
|
||||
// exceptions (minimal)
|
||||
OP_TRY_PUSH, // operand = handler ip; push handler onto try-stack
|
||||
OP_TRY_POP, // pop current handler
|
||||
OP_THROW, // pops error value; if handler -> jump to it (push err), else print and terminate
|
||||
// exceptions (minimal)
|
||||
OP_TRY_PUSH, // operand = handler ip; push handler onto try-stack
|
||||
OP_TRY_POP, // pop current handler
|
||||
OP_THROW, // pops error value; if handler -> jump to it (push err), else print and terminate
|
||||
|
||||
// C99 math.h rounding family (float-aware)
|
||||
OP_FLOOR, // pops x (int/float); pushes floor(x) (int if integral else float)
|
||||
OP_CEIL, // pops x (int/float); pushes ceil(x) (int if integral else float)
|
||||
OP_TRUNC, // pops x (int/float); pushes trunc(x) (int if integral else float)
|
||||
OP_ROUND, // pops x (int/float); pushes round(x) (half away from zero)
|
||||
// C99 math.h rounding family (float-aware)
|
||||
OP_FLOOR, // pops x (int/float); pushes floor(x) (int if integral else float)
|
||||
OP_CEIL, // pops x (int/float); pushes ceil(x) (int if integral else float)
|
||||
OP_TRUNC, // pops x (int/float); pushes trunc(x) (int if integral else float)
|
||||
OP_ROUND, // pops x (int/float); pushes round(x) (half away from zero)
|
||||
|
||||
// C99 math.h transcendentals (float-aware)
|
||||
OP_SIN, // pops x (int/float); pushes sin(x)
|
||||
OP_COS, // pops x (int/float); pushes cos(x)
|
||||
OP_TAN, // pops x (int/float); pushes tan(x)
|
||||
OP_EXP, // pops x (int/float); pushes exp(x)
|
||||
OP_LOG, // pops x (int/float); pushes natural log ln(x)
|
||||
OP_LOG10, // pops x (int/float); pushes log10(x)
|
||||
OP_SQRT, // pops x (int/float); pushes sqrt(x)
|
||||
// C99 math.h transcendentals (float-aware)
|
||||
OP_SIN, // pops x (int/float); pushes sin(x)
|
||||
OP_COS, // pops x (int/float); pushes cos(x)
|
||||
OP_TAN, // pops x (int/float); pushes tan(x)
|
||||
OP_EXP, // pops x (int/float); pushes exp(x)
|
||||
OP_LOG, // pops x (int/float); pushes natural log ln(x)
|
||||
OP_LOG10, // pops x (int/float); pushes log10(x)
|
||||
OP_SQRT, // pops x (int/float); pushes sqrt(x)
|
||||
|
||||
// Integer math helpers
|
||||
OP_GCD, // pops b, a; pushes gcd(|a|,|b|)
|
||||
OP_LCM, // pops b, a; pushes lcm(|a|,|b|) (0 if either is 0)
|
||||
OP_ISQRT, // pops x; pushes floor(sqrt(max(0,x))) for integers
|
||||
OP_SIGN, // pops x; pushes -1, 0, or 1 depending on the sign
|
||||
// Integer math helpers
|
||||
OP_GCD, // pops b, a; pushes gcd(|a|,|b|)
|
||||
OP_LCM, // pops b, a; pushes lcm(|a|,|b|) (0 if either is 0)
|
||||
OP_ISQRT, // pops x; pushes floor(sqrt(max(0,x))) for integers
|
||||
OP_SIGN, // pops x; pushes -1, 0, or 1 depending on the sign
|
||||
|
||||
// Min/Max variants (float-aware, C99 semantics)
|
||||
OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99)
|
||||
OP_FMAX, // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99)
|
||||
// Min/Max variants (float-aware, C99 semantics)
|
||||
OP_FMIN, // pops b, a (int/float); pushes fmin(a,b) (NaN handling per C99)
|
||||
OP_FMAX, // pops b, a (int/float); pushes fmax(a,b) (NaN handling per C99)
|
||||
|
||||
// Rust FFI demo opcode(s)
|
||||
OP_RUST_HELLO, // pushes string returned from Rust (hello world)
|
||||
OP_RUST_HELLO_ARGS, // pops message string; prints it via Rust; pushes Nil
|
||||
OP_RUST_HELLO_ARGS_RETURN, // pops message string; returns it from Rust without printing; pushes returned string
|
||||
OP_RUST_GET_SP, // pushes current VM stack pointer (via Rust reading VM memory)
|
||||
OP_RUST_SET_EXIT, // pops int and sets VM exit_code (via Rust writing VM memory)
|
||||
// Rust FFI demo opcode(s)
|
||||
OP_RUST_HELLO, // pushes string returned from Rust (hello world)
|
||||
OP_RUST_HELLO_ARGS, // pops message string; prints it via Rust; pushes Nil
|
||||
OP_RUST_HELLO_ARGS_RETURN, // pops message string; returns it from Rust without printing; pushes returned string
|
||||
OP_RUST_GET_SP, // pushes current VM stack pointer (via Rust reading VM memory)
|
||||
OP_RUST_SET_EXIT, // pops int and sets VM exit_code (via Rust writing VM memory)
|
||||
|
||||
// C++ demo opcode(s)
|
||||
OP_CPP_ADD, // pops b, a; pushes (a + b)
|
||||
// C++ demo opcode(s)
|
||||
OP_CPP_ADD, // pops b, a; pushes (a + b)
|
||||
|
||||
/* Notcurses TUI (optional) */
|
||||
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
|
||||
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
|
||||
OP_NC_CLEAR, // clears screen/plane; returns 0
|
||||
OP_NC_DRAW_TEXT, // pops text, x, y; draws; returns 0
|
||||
OP_NC_GETCH // pops timeout_ms; returns codepoint or -1 on timeout/error
|
||||
/* Notcurses TUI (optional) */
|
||||
OP_NC_INIT, // initializes Notcurses; returns 1 on success, 0 on failure
|
||||
OP_NC_SHUTDOWN, // shuts down Notcurses; returns 0
|
||||
OP_NC_CLEAR, // clears screen/plane; returns 0
|
||||
OP_NC_DRAW_TEXT, // pops text, x, y; draws; returns 0
|
||||
OP_NC_GETCH // pops timeout_ms; returns codepoint or -1 on timeout/error
|
||||
} OpCode;
|
||||
|
||||
typedef struct {
|
||||
OpCode op;
|
||||
int32_t operand;
|
||||
OpCode op;
|
||||
int32_t operand;
|
||||
} Instruction;
|
||||
|
||||
typedef struct Bytecode {
|
||||
Instruction *instructions;
|
||||
int instr_count;
|
||||
Instruction *instructions;
|
||||
int instr_count;
|
||||
|
||||
Value *constants;
|
||||
int const_count;
|
||||
Value *constants;
|
||||
int const_count;
|
||||
|
||||
/* debug metadata */
|
||||
const char *name; /* function or module name (optional) */
|
||||
const char *source_file; /* originating source filename (optional) */
|
||||
/* debug metadata */
|
||||
const char *name; /* function or module name (optional) */
|
||||
const char *source_file; /* originating source filename (optional) */
|
||||
} Bytecode;
|
||||
|
||||
// constructors / manipulation
|
||||
|
|
|
|||
15
src/external/curl.c
vendored
15
src/external/curl.c
vendored
|
|
@ -12,18 +12,23 @@
|
|||
/* Ensure libcurl headers and helpers are defined at file scope (not inside vm_run) */
|
||||
#ifdef FUN_WITH_CURL
|
||||
#include <curl/curl.h>
|
||||
typedef struct { char *d; size_t n; } FunCurlBuf;
|
||||
typedef struct {
|
||||
char *d;
|
||||
size_t n;
|
||||
} FunCurlBuf;
|
||||
static size_t fun_curl_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
|
||||
size_t add = sz * nm;
|
||||
FunCurlBuf *b = (FunCurlBuf*)ud;
|
||||
char *p = (char*)realloc(b->d, b->n + add + 1);
|
||||
FunCurlBuf *b = (FunCurlBuf *)ud;
|
||||
char *p = (char *)realloc(b->d, b->n + add + 1);
|
||||
if (!p) return 0;
|
||||
memcpy(p + b->n, ptr, add);
|
||||
b->d = p; b->n += add; b->d[b->n] = '\0';
|
||||
b->d = p;
|
||||
b->n += add;
|
||||
b->d[b->n] = '\0';
|
||||
return add;
|
||||
}
|
||||
static size_t fun_curl_file_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
|
||||
FILE *f = (FILE*)ud;
|
||||
FILE *f = (FILE *)ud;
|
||||
return fwrite(ptr, sz, nm, f);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
22
src/external/ini.c
vendored
22
src/external/ini.c
vendored
|
|
@ -11,18 +11,18 @@
|
|||
|
||||
#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 "vm/ini/handles.h"
|
||||
#endif
|
||||
|
|
|
|||
171
src/external/json.c
vendored
171
src/external/json.c
vendored
|
|
@ -9,7 +9,7 @@
|
|||
* Added: 2025-12-11 (2025-12-11 migrated from src/vm.c)
|
||||
*/
|
||||
|
||||
/* json-c helpers and VM opcode cases (included from vm.c) */
|
||||
/* json-c helpers and VM opcode cases (included from vm.c) */
|
||||
|
||||
#ifdef FUN_WITH_JSON
|
||||
#include "value.h"
|
||||
|
|
@ -20,88 +20,99 @@
|
|||
|
||||
/* --- Conversion helpers between json-c and Fun Value --- */
|
||||
static Value json_to_fun(json_object *j) {
|
||||
if (!j) return make_nil();
|
||||
enum json_type t = json_object_get_type(j);
|
||||
switch (t) {
|
||||
case json_type_null: return make_nil();
|
||||
case json_type_boolean: return make_bool(json_object_get_boolean(j));
|
||||
case json_type_double: return make_float(json_object_get_double(j));
|
||||
case json_type_int: return make_int((int64_t)json_object_get_int64(j));
|
||||
case json_type_string: return make_string(json_object_get_string(j));
|
||||
case json_type_array: {
|
||||
size_t n = json_object_array_length(j);
|
||||
if (n == 0) {
|
||||
return make_array_from_values(NULL, 0);
|
||||
}
|
||||
Value *vals = (Value*)malloc(sizeof(Value) * n);
|
||||
if (!vals) return make_array_from_values(NULL, 0);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
json_object *item = json_object_array_get_idx(j, (int)i);
|
||||
vals[i] = json_to_fun(item);
|
||||
}
|
||||
Value arr = make_array_from_values(vals, (int)n);
|
||||
for (size_t i = 0; i < n; ++i) free_value(vals[i]);
|
||||
free(vals);
|
||||
return arr;
|
||||
}
|
||||
case json_type_object: {
|
||||
Value map = make_map_empty();
|
||||
json_object_object_foreach(j, key, val) {
|
||||
(void)map_set(&map, key, json_to_fun(val));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
default:
|
||||
return make_nil();
|
||||
if (!j) return make_nil();
|
||||
enum json_type t = json_object_get_type(j);
|
||||
switch (t) {
|
||||
case json_type_null:
|
||||
return make_nil();
|
||||
case json_type_boolean:
|
||||
return make_bool(json_object_get_boolean(j));
|
||||
case json_type_double:
|
||||
return make_float(json_object_get_double(j));
|
||||
case json_type_int:
|
||||
return make_int((int64_t)json_object_get_int64(j));
|
||||
case json_type_string:
|
||||
return make_string(json_object_get_string(j));
|
||||
case json_type_array: {
|
||||
size_t n = json_object_array_length(j);
|
||||
if (n == 0) {
|
||||
return make_array_from_values(NULL, 0);
|
||||
}
|
||||
Value *vals = (Value *)malloc(sizeof(Value) * n);
|
||||
if (!vals) return make_array_from_values(NULL, 0);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
json_object *item = json_object_array_get_idx(j, (int)i);
|
||||
vals[i] = json_to_fun(item);
|
||||
}
|
||||
Value arr = make_array_from_values(vals, (int)n);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
free_value(vals[i]);
|
||||
free(vals);
|
||||
return arr;
|
||||
}
|
||||
case json_type_object: {
|
||||
Value map = make_map_empty();
|
||||
json_object_object_foreach(j, key, val) {
|
||||
(void)map_set(&map, key, json_to_fun(val));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
default:
|
||||
return make_nil();
|
||||
}
|
||||
}
|
||||
|
||||
static json_object* fun_to_json(const Value *v) {
|
||||
switch (v->type) {
|
||||
case VAL_NIL: return json_object_new_null();
|
||||
case VAL_BOOL: return json_object_new_boolean(v->i ? 1 : 0);
|
||||
case VAL_INT: return json_object_new_int64(v->i);
|
||||
case VAL_FLOAT: return json_object_new_double(v->d);
|
||||
case VAL_STRING: return json_object_new_string(v->s ? v->s : "");
|
||||
case VAL_ARRAY: {
|
||||
json_object *arr = json_object_new_array();
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
json_object_array_add(arr, fun_to_json(&item));
|
||||
free_value(item);
|
||||
} else {
|
||||
json_object_array_add(arr, json_object_new_null());
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
json_object *obj = json_object_new_object();
|
||||
/* We don't have an iterator API; use keys() helper */
|
||||
Value keys = map_keys_array(v);
|
||||
int kn = array_length(&keys);
|
||||
for (int i = 0; i < kn; ++i) {
|
||||
Value k;
|
||||
if (!array_get_copy(&keys, i, &k)) continue;
|
||||
if (k.type == VAL_STRING && k.s) {
|
||||
Value val;
|
||||
if (map_get_copy(v, k.s, &val)) {
|
||||
json_object_object_add(obj, k.s, fun_to_json(&val));
|
||||
free_value(val);
|
||||
} else {
|
||||
json_object_object_add(obj, k.s, json_object_new_null());
|
||||
}
|
||||
}
|
||||
free_value(k);
|
||||
}
|
||||
free_value(keys);
|
||||
return obj;
|
||||
}
|
||||
default:
|
||||
/* Fallback: stringify unsupported types */
|
||||
return json_object_new_string("<unsupported>");
|
||||
static json_object *fun_to_json(const Value *v) {
|
||||
switch (v->type) {
|
||||
case VAL_NIL:
|
||||
return json_object_new_null();
|
||||
case VAL_BOOL:
|
||||
return json_object_new_boolean(v->i ? 1 : 0);
|
||||
case VAL_INT:
|
||||
return json_object_new_int64(v->i);
|
||||
case VAL_FLOAT:
|
||||
return json_object_new_double(v->d);
|
||||
case VAL_STRING:
|
||||
return json_object_new_string(v->s ? v->s : "");
|
||||
case VAL_ARRAY: {
|
||||
json_object *arr = json_object_new_array();
|
||||
int n = array_length(v);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (array_get_copy(v, i, &item)) {
|
||||
json_object_array_add(arr, fun_to_json(&item));
|
||||
free_value(item);
|
||||
} else {
|
||||
json_object_array_add(arr, json_object_new_null());
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
json_object *obj = json_object_new_object();
|
||||
/* We don't have an iterator API; use keys() helper */
|
||||
Value keys = map_keys_array(v);
|
||||
int kn = array_length(&keys);
|
||||
for (int i = 0; i < kn; ++i) {
|
||||
Value k;
|
||||
if (!array_get_copy(&keys, i, &k)) continue;
|
||||
if (k.type == VAL_STRING && k.s) {
|
||||
Value val;
|
||||
if (map_get_copy(v, k.s, &val)) {
|
||||
json_object_object_add(obj, k.s, fun_to_json(&val));
|
||||
free_value(val);
|
||||
} else {
|
||||
json_object_object_add(obj, k.s, json_object_new_null());
|
||||
}
|
||||
}
|
||||
free_value(k);
|
||||
}
|
||||
free_value(keys);
|
||||
return obj;
|
||||
}
|
||||
default:
|
||||
/* Fallback: stringify unsupported types */
|
||||
return json_object_new_string("<unsupported>");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
242
src/external/libressl.c
vendored
242
src/external/libressl.c
vendored
|
|
@ -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>
|
||||
|
|
@ -33,140 +33,164 @@ int EVP_MD_get_size(const EVP_MD *md);
|
|||
|
||||
/* Compute MD5 hex of input buffer; returns malloc'ed C string (lowercase hex). */
|
||||
static char *fun_libressl_md5_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_LIBRESSL
|
||||
const EVP_MD *md = EVP_md5();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_md5();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compute SHA-256 hex of input buffer; returns malloc'ed C string (lowercase hex). */
|
||||
static char *fun_libressl_sha256_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_LIBRESSL
|
||||
const EVP_MD *md = EVP_sha256();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_sha256();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compute SHA-512 hex of input buffer; returns malloc'ed C string (lowercase hex). */
|
||||
static char *fun_libressl_sha512_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_LIBRESSL
|
||||
const EVP_MD *md = EVP_sha512();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_sha512();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compute RIPEMD-160 hex of input buffer; returns malloc'ed C string (lowercase hex). */
|
||||
static char *fun_libressl_ripemd160_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_LIBRESSL
|
||||
const EVP_MD *md = EVP_ripemd160();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_ripemd160();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
51
src/external/libsql.c
vendored
51
src/external/libsql.c
vendored
|
|
@ -10,46 +10,49 @@
|
|||
*/
|
||||
|
||||
#ifdef FUN_WITH_LIBSQL
|
||||
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
|
||||
|
||||
typedef struct LibSqlHandle {
|
||||
int id;
|
||||
sqlite3 *db;
|
||||
struct LibSqlHandle *next;
|
||||
int id;
|
||||
sqlite3 *db;
|
||||
struct LibSqlHandle *next;
|
||||
} LibSqlHandle;
|
||||
|
||||
static LibSqlHandle *g_libsql_handles = NULL;
|
||||
static int g_libsql_next_id = 1;
|
||||
|
||||
static LibSqlHandle *libsql_reg_add(sqlite3 *db) {
|
||||
LibSqlHandle *h = (LibSqlHandle*)malloc(sizeof(LibSqlHandle));
|
||||
if (!h) return NULL;
|
||||
h->id = g_libsql_next_id++;
|
||||
h->db = db;
|
||||
h->next = g_libsql_handles;
|
||||
g_libsql_handles = h;
|
||||
return h;
|
||||
LibSqlHandle *h = (LibSqlHandle *)malloc(sizeof(LibSqlHandle));
|
||||
if (!h) return NULL;
|
||||
h->id = g_libsql_next_id++;
|
||||
h->db = db;
|
||||
h->next = g_libsql_handles;
|
||||
g_libsql_handles = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
static LibSqlHandle *libsql_reg_get(int id) {
|
||||
LibSqlHandle *p = g_libsql_handles;
|
||||
while (p) { if (p->id == id) return p; p = p->next; }
|
||||
return NULL;
|
||||
LibSqlHandle *p = g_libsql_handles;
|
||||
while (p) {
|
||||
if (p->id == id) return p;
|
||||
p = p->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void libsql_reg_del(int id) {
|
||||
LibSqlHandle **pp = &g_libsql_handles;
|
||||
while (*pp) {
|
||||
if ((*pp)->id == id) {
|
||||
LibSqlHandle *dead = *pp;
|
||||
*pp = (*pp)->next;
|
||||
free(dead);
|
||||
return;
|
||||
}
|
||||
pp = &((*pp)->next);
|
||||
LibSqlHandle **pp = &g_libsql_handles;
|
||||
while (*pp) {
|
||||
if ((*pp)->id == id) {
|
||||
LibSqlHandle *dead = *pp;
|
||||
*pp = (*pp)->next;
|
||||
free(dead);
|
||||
return;
|
||||
}
|
||||
pp = &((*pp)->next);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
246
src/external/openssl.c
vendored
246
src/external/openssl.c
vendored
|
|
@ -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>
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
* Added: 2026-02-19
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* OpenSSL integration helpers (MD5, RIPEMD-160, SHA-256, SHA-512)
|
||||
*/
|
||||
|
||||
|
|
@ -28,109 +28,127 @@ int EVP_MD_get_size(const EVP_MD *md);
|
|||
* returns an allocated empty string ("") to keep behavior consistent with
|
||||
* other optional extensions. */
|
||||
static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_OPENSSL
|
||||
const EVP_MD *md = EVP_md5();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
// EVP_Digest handles zero-length fine as well
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_md5();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
// EVP_Digest handles zero-length fine as well
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compute SHA-256 hex of input buffer; returns malloc'ed C string (lowercase hex).
|
||||
* Fallback when OpenSSL disabled: empty string. */
|
||||
static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_OPENSSL
|
||||
const EVP_MD *md = EVP_sha256();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_sha256();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Compute SHA-512 hex of input buffer; returns malloc'ed C string (lowercase hex).
|
||||
* Fallback when OpenSSL disabled: empty string. */
|
||||
static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_OPENSSL
|
||||
const EVP_MD *md = EVP_sha512();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_sha512();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -139,35 +157,41 @@ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
|
|||
* EVP_ripemd160() can return NULL. In that case we return NULL and the VM opcode
|
||||
* will fall back to empty string behavior. When OpenSSL is disabled, return empty string. */
|
||||
static char *fun_openssl_ripemd160_hex(const unsigned char *data, size_t len) {
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
static const char hexdig[] = "0123456789abcdef";
|
||||
if (!data && len != 0) return NULL;
|
||||
#ifdef FUN_WITH_OPENSSL
|
||||
const EVP_MD *md = EVP_ripemd160();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) { free(digest); return NULL; }
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
const EVP_MD *md = EVP_ripemd160();
|
||||
if (!md) return NULL;
|
||||
int dlen = EVP_MD_get_size(md);
|
||||
if (dlen <= 0) return NULL;
|
||||
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||
if (!digest) return NULL;
|
||||
unsigned int out_len = 0;
|
||||
int ok;
|
||||
if (len == 0) {
|
||||
ok = EVP_Digest(NULL, 0, digest, &out_len, md, NULL);
|
||||
} else {
|
||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||
}
|
||||
if (ok != 1 || (int)out_len != dlen) {
|
||||
free(digest);
|
||||
return hex;
|
||||
return NULL;
|
||||
}
|
||||
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||
if (!hex) {
|
||||
free(digest);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < dlen; ++i) {
|
||||
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||
}
|
||||
hex[dlen * 2] = '\0';
|
||||
free(digest);
|
||||
return hex;
|
||||
#else
|
||||
char *hex = (char*)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
char *hex = (char *)malloc(1);
|
||||
if (hex) hex[0] = '\0';
|
||||
return hex;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
4
src/external/pcre2.c
vendored
4
src/external/pcre2.c
vendored
|
|
@ -8,8 +8,8 @@
|
|||
*
|
||||
* Added: 2025-12-11 (2025-12-11 migrated from src/vm/libsql/common.c)
|
||||
*/
|
||||
|
||||
/* Ensure PCRE2 is configured consistently across the whole translation unit.
|
||||
|
||||
/* Ensure PCRE2 is configured consistently across the whole translation unit.
|
||||
* vm.c includes many opcode implementation .c files; some use PCRE2. For PCRE2
|
||||
* headers to expose the correct typedefs (e.g., pcre2_code, PCRE2_SPTR), the
|
||||
* PCRE2_CODE_UNIT_WIDTH macro must be defined before the first inclusion of
|
||||
|
|
|
|||
89
src/external/pcsc.c
vendored
89
src/external/pcsc.c
vendored
|
|
@ -14,63 +14,72 @@ PCSC helpers: registries and helper functions.
|
|||
Included the file scope from vm.c.
|
||||
*/
|
||||
|
||||
#ifdef FUN_WITH_PCSC
|
||||
#ifdef FUN_WITH_PCSC
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<PCSC/winscard.h>)
|
||||
#include <PCSC/winscard.h>
|
||||
#include <PCSC/wintypes.h>
|
||||
#elif __has_include(<winscard.h>)
|
||||
#include <winscard.h>
|
||||
#else
|
||||
#error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
|
||||
#endif
|
||||
#else
|
||||
#include <PCSC/winscard.h>
|
||||
#include <PCSC/wintypes.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
SCARDCONTEXT ctx;
|
||||
int in_use;
|
||||
} pcsc_ctx_entry;
|
||||
#if __has_include(<PCSC/winscard.h>)
|
||||
#include <PCSC/winscard.h>
|
||||
#include <PCSC/wintypes.h>
|
||||
#elif __has_include(<winscard.h>)
|
||||
#include <winscard.h>
|
||||
#else
|
||||
#error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
|
||||
#endif
|
||||
#else
|
||||
#include <PCSC/winscard.h>
|
||||
#include <PCSC/wintypes.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
SCARDHANDLE h;
|
||||
DWORD proto;
|
||||
int in_use;
|
||||
SCARDCONTEXT ctx;
|
||||
int in_use;
|
||||
} pcsc_ctx_entry;
|
||||
|
||||
typedef struct {
|
||||
SCARDHANDLE h;
|
||||
DWORD proto;
|
||||
int in_use;
|
||||
} pcsc_card_entry;
|
||||
|
||||
static pcsc_ctx_entry g_pcsc_ctx[8];
|
||||
static pcsc_card_entry g_pcsc_card[32];
|
||||
|
||||
static int pcsc_alloc_ctx_slot(void) {
|
||||
for (int i = 0; i < (int)(sizeof(g_pcsc_ctx)/sizeof(g_pcsc_ctx[0])); ++i) {
|
||||
if (!g_pcsc_ctx[i].in_use) { g_pcsc_ctx[i].in_use = 1; g_pcsc_ctx[i].ctx = 0; return i + 1; }
|
||||
for (int i = 0; i < (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0])); ++i) {
|
||||
if (!g_pcsc_ctx[i].in_use) {
|
||||
g_pcsc_ctx[i].in_use = 1;
|
||||
g_pcsc_ctx[i].ctx = 0;
|
||||
return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcsc_alloc_card_slot(void) {
|
||||
for (int i = 0; i < (int)(sizeof(g_pcsc_card)/sizeof(g_pcsc_card[0])); ++i) {
|
||||
if (!g_pcsc_card[i].in_use) { g_pcsc_card[i].in_use = 1; g_pcsc_card[i].h = 0; g_pcsc_card[i].proto = 0; return i + 1; }
|
||||
for (int i = 0; i < (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0])); ++i) {
|
||||
if (!g_pcsc_card[i].in_use) {
|
||||
g_pcsc_card[i].in_use = 1;
|
||||
g_pcsc_card[i].h = 0;
|
||||
g_pcsc_card[i].proto = 0;
|
||||
return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pcsc_ctx_entry* pcsc_get_ctx(int id) {
|
||||
if (id <= 0) return NULL;
|
||||
int idx = id - 1;
|
||||
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx)/sizeof(g_pcsc_ctx[0]))) return NULL;
|
||||
if (!g_pcsc_ctx[idx].in_use) return NULL;
|
||||
return &g_pcsc_ctx[idx];
|
||||
static pcsc_ctx_entry *pcsc_get_ctx(int id) {
|
||||
if (id <= 0) return NULL;
|
||||
int idx = id - 1;
|
||||
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0]))) return NULL;
|
||||
if (!g_pcsc_ctx[idx].in_use) return NULL;
|
||||
return &g_pcsc_ctx[idx];
|
||||
}
|
||||
|
||||
static pcsc_card_entry* pcsc_get_card(int id) {
|
||||
if (id <= 0) return NULL;
|
||||
int idx = id - 1;
|
||||
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card)/sizeof(g_pcsc_card[0]))) return NULL;
|
||||
if (!g_pcsc_card[idx].in_use) return NULL;
|
||||
return &g_pcsc_card[idx];
|
||||
static pcsc_card_entry *pcsc_get_card(int id) {
|
||||
if (id <= 0) return NULL;
|
||||
int idx = id - 1;
|
||||
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0]))) return NULL;
|
||||
if (!g_pcsc_card[idx].in_use) return NULL;
|
||||
return &g_pcsc_card[idx];
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
42
src/external/sqlite.c
vendored
42
src/external/sqlite.c
vendored
|
|
@ -16,34 +16,40 @@
|
|||
#include <sqlite3.h>
|
||||
|
||||
typedef struct SqlHandle {
|
||||
int id;
|
||||
sqlite3 *db;
|
||||
struct SqlHandle *next;
|
||||
int id;
|
||||
sqlite3 *db;
|
||||
struct SqlHandle *next;
|
||||
} SqlHandle;
|
||||
|
||||
static SqlHandle *g_sql_handles = NULL;
|
||||
static int g_sql_next_id = 1;
|
||||
|
||||
static SqlHandle* sql_reg_add(sqlite3 *db) {
|
||||
SqlHandle *h = (SqlHandle*)calloc(1, sizeof(SqlHandle));
|
||||
if (!h) return NULL;
|
||||
h->id = g_sql_next_id++;
|
||||
h->db = db;
|
||||
h->next = g_sql_handles;
|
||||
g_sql_handles = h;
|
||||
return h;
|
||||
static SqlHandle *sql_reg_add(sqlite3 *db) {
|
||||
SqlHandle *h = (SqlHandle *)calloc(1, sizeof(SqlHandle));
|
||||
if (!h) return NULL;
|
||||
h->id = g_sql_next_id++;
|
||||
h->db = db;
|
||||
h->next = g_sql_handles;
|
||||
g_sql_handles = h;
|
||||
return h;
|
||||
}
|
||||
|
||||
static SqlHandle* sql_reg_get(int id) {
|
||||
for (SqlHandle *p = g_sql_handles; p; p = p->next) if (p->id == id) return p;
|
||||
return NULL;
|
||||
static SqlHandle *sql_reg_get(int id) {
|
||||
for (SqlHandle *p = g_sql_handles; p; p = p->next)
|
||||
if (p->id == id) return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sql_reg_del(int id) {
|
||||
SqlHandle **pp = &g_sql_handles;
|
||||
while (*pp) {
|
||||
if ((*pp)->id == id) { SqlHandle *d = *pp; *pp = d->next; free(d); return; }
|
||||
pp = &(*pp)->next;
|
||||
SqlHandle **pp = &g_sql_handles;
|
||||
while (*pp) {
|
||||
if ((*pp)->id == id) {
|
||||
SqlHandle *d = *pp;
|
||||
*pp = d->next;
|
||||
free(d);
|
||||
return;
|
||||
}
|
||||
pp = &(*pp)->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
82
src/external/tcltk.c
vendored
82
src/external/tcltk.c
vendored
|
|
@ -12,58 +12,68 @@
|
|||
#ifdef FUN_WITH_TCLTK
|
||||
#include <tcl.h>
|
||||
#include <tk.h>
|
||||
static Tcl_Interp* g_fun_tcl_interp = NULL;
|
||||
static Tcl_Interp *g_fun_tcl_interp = NULL;
|
||||
|
||||
static void fun_tk_init_once(void) {
|
||||
if (g_fun_tcl_interp) return;
|
||||
Tcl_FindExecutable(NULL);
|
||||
g_fun_tcl_interp = Tcl_CreateInterp();
|
||||
if (!g_fun_tcl_interp) return;
|
||||
if (Tcl_Init(g_fun_tcl_interp) != TCL_OK) {
|
||||
fprintf(stderr, "Tcl_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
|
||||
}
|
||||
if (Tk_Init(g_fun_tcl_interp) != TCL_OK) {
|
||||
fprintf(stderr, "Tk_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
|
||||
}
|
||||
/* Ensure the app terminates if the main window is closed via window manager */
|
||||
/* Best-effort: set WM_DELETE_WINDOW handler to exit the process. */
|
||||
Tcl_Eval(g_fun_tcl_interp, "wm protocol . WM_DELETE_WINDOW {exit 0}");
|
||||
if (g_fun_tcl_interp) return;
|
||||
Tcl_FindExecutable(NULL);
|
||||
g_fun_tcl_interp = Tcl_CreateInterp();
|
||||
if (!g_fun_tcl_interp) return;
|
||||
if (Tcl_Init(g_fun_tcl_interp) != TCL_OK) {
|
||||
fprintf(stderr, "Tcl_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
|
||||
}
|
||||
if (Tk_Init(g_fun_tcl_interp) != TCL_OK) {
|
||||
fprintf(stderr, "Tk_Init failed: %s\n", Tcl_GetStringResult(g_fun_tcl_interp));
|
||||
}
|
||||
/* Ensure the app terminates if the main window is closed via window manager */
|
||||
/* Best-effort: set WM_DELETE_WINDOW handler to exit the process. */
|
||||
Tcl_Eval(g_fun_tcl_interp, "wm protocol . WM_DELETE_WINDOW {exit 0}");
|
||||
}
|
||||
|
||||
static int fun_tk_eval_script(const char *script) {
|
||||
fun_tk_init_once();
|
||||
if (!g_fun_tcl_interp) return -1;
|
||||
int rc = Tcl_Eval(g_fun_tcl_interp, script ? script : "");
|
||||
return rc; /* TCL_OK = 0 */
|
||||
fun_tk_init_once();
|
||||
if (!g_fun_tcl_interp) return -1;
|
||||
int rc = Tcl_Eval(g_fun_tcl_interp, script ? script : "");
|
||||
return rc; /* TCL_OK = 0 */
|
||||
}
|
||||
|
||||
static const char* fun_tk_get_result(void) {
|
||||
fun_tk_init_once();
|
||||
if (!g_fun_tcl_interp) return "";
|
||||
return Tcl_GetStringResult(g_fun_tcl_interp);
|
||||
static const char *fun_tk_get_result(void) {
|
||||
fun_tk_init_once();
|
||||
if (!g_fun_tcl_interp) return "";
|
||||
return Tcl_GetStringResult(g_fun_tcl_interp);
|
||||
}
|
||||
|
||||
static void fun_tk_loop(void) {
|
||||
fun_tk_init_once();
|
||||
if (!g_fun_tcl_interp) return;
|
||||
/* Drive Tk event loop until all main windows are closed */
|
||||
while (Tk_GetNumMainWindows() > 0) {
|
||||
while (Tcl_DoOneEvent(0)) {}
|
||||
/* tiny sleep to avoid busy spin */
|
||||
fun_tk_init_once();
|
||||
if (!g_fun_tcl_interp) return;
|
||||
/* Drive Tk event loop until all main windows are closed */
|
||||
while (Tk_GetNumMainWindows() > 0) {
|
||||
while (Tcl_DoOneEvent(0)) {
|
||||
}
|
||||
/* tiny sleep to avoid busy spin */
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
Sleep(1);
|
||||
Sleep(1);
|
||||
#else
|
||||
#include <time.h>
|
||||
struct timespec ts = {0, 1000000}; /* 1 ms */
|
||||
nanosleep(&ts, NULL);
|
||||
struct timespec ts = {0, 1000000}; /* 1 ms */
|
||||
nanosleep(&ts, NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Stubs when Tcl/Tk is disabled */
|
||||
static void fun_tk_init_once(void) { (void)0; }
|
||||
static int fun_tk_eval_script(const char *script) { (void)script; return -1; }
|
||||
static const char* fun_tk_get_result(void) { return ""; }
|
||||
static void fun_tk_loop(void) { (void)0; }
|
||||
static void fun_tk_init_once(void) {
|
||||
(void)0;
|
||||
}
|
||||
static int fun_tk_eval_script(const char *script) {
|
||||
(void)script;
|
||||
return -1;
|
||||
}
|
||||
static const char *fun_tk_get_result(void) {
|
||||
return "";
|
||||
}
|
||||
static void fun_tk_loop(void) {
|
||||
(void)0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
34
src/external/xml2.c
vendored
34
src/external/xml2.c
vendored
|
|
@ -13,24 +13,34 @@
|
|||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
typedef struct { xmlDocPtr doc; int in_use; } XmlDocSlot;
|
||||
typedef struct { xmlNodePtr node; int in_use; } XmlNodeSlot;
|
||||
typedef struct {
|
||||
xmlDocPtr doc;
|
||||
int in_use;
|
||||
} XmlDocSlot;
|
||||
typedef struct {
|
||||
xmlNodePtr node;
|
||||
int in_use;
|
||||
} XmlNodeSlot;
|
||||
|
||||
static XmlDocSlot g_xml_docs[64];
|
||||
static XmlNodeSlot g_xml_nodes[256];
|
||||
|
||||
static int xml_doc_alloc(xmlDocPtr d) {
|
||||
for (int i = 1; i < (int)(sizeof(g_xml_docs)/sizeof(g_xml_docs[0])); ++i) {
|
||||
if (!g_xml_docs[i].in_use) { g_xml_docs[i].in_use = 1; g_xml_docs[i].doc = d; return i; }
|
||||
for (int i = 1; i < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])); ++i) {
|
||||
if (!g_xml_docs[i].in_use) {
|
||||
g_xml_docs[i].in_use = 1;
|
||||
g_xml_docs[i].doc = d;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static xmlDocPtr xml_doc_get(int h) {
|
||||
if (h > 0 && h < (int)(sizeof(g_xml_docs)/sizeof(g_xml_docs[0])) && g_xml_docs[h].in_use) return g_xml_docs[h].doc;
|
||||
if (h > 0 && h < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) && g_xml_docs[h].in_use) return g_xml_docs[h].doc;
|
||||
return NULL;
|
||||
}
|
||||
static int xml_doc_free_handle(int h) {
|
||||
if (h <= 0 || h >= (int)(sizeof(g_xml_docs)/sizeof(g_xml_docs[0])) || !g_xml_docs[h].in_use) return 0;
|
||||
if (h <= 0 || h >= (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) || !g_xml_docs[h].in_use) return 0;
|
||||
if (g_xml_docs[h].doc) xmlFreeDoc(g_xml_docs[h].doc);
|
||||
g_xml_docs[h].doc = NULL;
|
||||
g_xml_docs[h].in_use = 0;
|
||||
|
|
@ -38,17 +48,21 @@ static int xml_doc_free_handle(int h) {
|
|||
}
|
||||
|
||||
static int xml_node_alloc(xmlNodePtr n) {
|
||||
for (int i = 1; i < (int)(sizeof(g_xml_nodes)/sizeof(g_xml_nodes[0])); ++i) {
|
||||
if (!g_xml_nodes[i].in_use) { g_xml_nodes[i].in_use = 1; g_xml_nodes[i].node = n; return i; }
|
||||
for (int i = 1; i < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])); ++i) {
|
||||
if (!g_xml_nodes[i].in_use) {
|
||||
g_xml_nodes[i].in_use = 1;
|
||||
g_xml_nodes[i].node = n;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static xmlNodePtr xml_node_get(int h) {
|
||||
if (h > 0 && h < (int)(sizeof(g_xml_nodes)/sizeof(g_xml_nodes[0])) && g_xml_nodes[h].in_use) return g_xml_nodes[h].node;
|
||||
if (h > 0 && h < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) && g_xml_nodes[h].in_use) return g_xml_nodes[h].node;
|
||||
return NULL;
|
||||
}
|
||||
static int xml_node_free_handle(int h) {
|
||||
if (h <= 0 || h >= (int)(sizeof(g_xml_nodes)/sizeof(g_xml_nodes[0])) || !g_xml_nodes[h].in_use) return 0;
|
||||
if (h <= 0 || h >= (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) || !g_xml_nodes[h].in_use) return 0;
|
||||
/* nodes are owned by their document; do not free here */
|
||||
g_xml_nodes[h].node = NULL;
|
||||
g_xml_nodes[h].in_use = 0;
|
||||
|
|
|
|||
212
src/fun.c
212
src/fun.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>
|
||||
|
|
@ -7,15 +7,15 @@
|
|||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* Main entry point for the Fun language interpreter.
|
||||
* Builds a CLI that runs a script file if provided; otherwise starts the REPL
|
||||
* when compiled with FUN_WITH_REPL enabled.
|
||||
*/
|
||||
|
||||
#include "bytecode.h"
|
||||
#include "vm.h"
|
||||
#include "parser.h"
|
||||
#include "vm.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -30,127 +30,127 @@
|
|||
#endif
|
||||
|
||||
static void print_usage(const char *prog) {
|
||||
printf("Fun %s\n", FUN_VERSION);
|
||||
printf("Usage:\n");
|
||||
printf("Fun %s\n", FUN_VERSION);
|
||||
printf("Usage:\n");
|
||||
#ifdef FUN_WITH_REPL
|
||||
printf(" %s [--trace|-t] [--repl-on-error] [script.fun]\n", prog ? prog : "fun");
|
||||
printf(" %s --help | -h\n", prog ? prog : "fun");
|
||||
printf(" %s --version | -V\n", prog ? prog : "fun");
|
||||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" --trace, -t Print executed ops and stack tops during run\n");
|
||||
printf(" --repl-on-error Enter interactive REPL on runtime error with stack preserved\n\n");
|
||||
printf("When no script is provided, a REPL starts. Submit an empty line to execute the buffer.\n");
|
||||
printf(" %s [--trace|-t] [--repl-on-error] [script.fun]\n", prog ? prog : "fun");
|
||||
printf(" %s --help | -h\n", prog ? prog : "fun");
|
||||
printf(" %s --version | -V\n", prog ? prog : "fun");
|
||||
printf("\n");
|
||||
printf("Options:\n");
|
||||
printf(" --trace, -t Print executed ops and stack tops during run\n");
|
||||
printf(" --repl-on-error Enter interactive REPL on runtime error with stack preserved\n\n");
|
||||
printf("When no script is provided, a REPL starts. Submit an empty line to execute the buffer.\n");
|
||||
#else
|
||||
printf(" %s [--trace|-t] <script.fun>\n", prog ? prog : "fun");
|
||||
printf(" %s --help | -h\n", prog ? prog : "fun");
|
||||
printf(" %s --version | -V\n", prog ? prog : "fun");
|
||||
printf("\n");
|
||||
printf("Options:\n --trace, -t Print executed ops and stack tops during run\n\n");
|
||||
printf("REPL is disabled in this build. Please provide a script file to run.\n");
|
||||
printf(" %s [--trace|-t] <script.fun>\n", prog ? prog : "fun");
|
||||
printf(" %s --help | -h\n", prog ? prog : "fun");
|
||||
printf(" %s --version | -V\n", prog ? prog : "fun");
|
||||
printf("\n");
|
||||
printf("Options:\n --trace, -t Print executed ops and stack tops during run\n\n");
|
||||
printf("REPL is disabled in this build. Please provide a script file to run.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
/* Set FUN_EXECUTABLE environment variable to the path of this binary */
|
||||
setenv("FUN_EXECUTABLE", argv[0], 1);
|
||||
/* Set FUN_EXECUTABLE environment variable to the path of this binary */
|
||||
setenv("FUN_EXECUTABLE", argv[0], 1);
|
||||
|
||||
VM vm;
|
||||
vm_init(&vm);
|
||||
VM vm;
|
||||
vm_init(&vm);
|
||||
|
||||
int argi = 1;
|
||||
for (; argi < argc; ++argi) {
|
||||
const char *arg = argv[argi];
|
||||
if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) {
|
||||
printf("Fun %s\n", FUN_VERSION);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(arg, "--trace") == 0 || strcmp(arg, "-t") == 0) {
|
||||
vm.trace_enabled = 1;
|
||||
continue;
|
||||
}
|
||||
#ifdef FUN_WITH_REPL
|
||||
if (strcmp(arg, "--repl-on-error") == 0) {
|
||||
vm.repl_on_error = 1;
|
||||
vm.on_error_repl = fun_run_repl; /* provide REPL entry to core VM */
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* first non-option assumed to be script path */
|
||||
break;
|
||||
int argi = 1;
|
||||
for (; argi < argc; ++argi) {
|
||||
const char *arg = argv[argi];
|
||||
if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(arg, "--version") == 0 || strcmp(arg, "-V") == 0) {
|
||||
printf("Fun %s\n", FUN_VERSION);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(arg, "--trace") == 0 || strcmp(arg, "-t") == 0) {
|
||||
vm.trace_enabled = 1;
|
||||
continue;
|
||||
}
|
||||
#ifdef FUN_WITH_REPL
|
||||
if (strcmp(arg, "--repl-on-error") == 0) {
|
||||
vm.repl_on_error = 1;
|
||||
vm.on_error_repl = fun_run_repl; /* provide REPL entry to core VM */
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
/* first non-option assumed to be script path */
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef FUN_WITH_REPL
|
||||
if (argi >= argc) {
|
||||
fprintf(stderr, "Error: REPL is disabled. Please provide a script to run.\n");
|
||||
print_usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
if (argi >= argc) {
|
||||
fprintf(stderr, "Error: REPL is disabled. Please provide a script to run.\n");
|
||||
print_usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (argi < argc) {
|
||||
const char *path = argv[argi];
|
||||
if (argi < argc) {
|
||||
const char *path = argv[argi];
|
||||
|
||||
/* Collect script arguments (everything after script path) and expose via env vars */
|
||||
int sargi = argi + 1; /* first script arg following the script path */
|
||||
int sargc = (sargi < argc) ? (argc - sargi) : 0;
|
||||
/* Collect script arguments (everything after script path) and expose via env vars */
|
||||
int sargi = argi + 1; /* first script arg following the script path */
|
||||
int sargc = (sargi < argc) ? (argc - sargi) : 0;
|
||||
|
||||
/* Export FUN_ARGC */
|
||||
{
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%d", sargc);
|
||||
setenv("FUN_ARGC", buf, 1);
|
||||
}
|
||||
|
||||
/* Export FUN_ARGV_i */
|
||||
for (int i = 0; i < sargc; ++i) {
|
||||
char key[32];
|
||||
snprintf(key, sizeof(key), "FUN_ARGV_%d", i);
|
||||
setenv(key, argv[sargi + i], 1);
|
||||
}
|
||||
|
||||
/* Optional: space-joined convenience string FUN_ARGS */
|
||||
if (sargc > 0) {
|
||||
size_t total = 0;
|
||||
for (int i = 0; i < sargc; ++i) {
|
||||
total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */
|
||||
}
|
||||
char *joined = (char*)malloc(total);
|
||||
if (joined) {
|
||||
joined[0] = '\0';
|
||||
for (int i = 0; i < sargc; ++i) {
|
||||
strcat(joined, argv[sargi + i]);
|
||||
if (i + 1 < sargc) strcat(joined, " ");
|
||||
}
|
||||
setenv("FUN_ARGS", joined, 1);
|
||||
free(joined);
|
||||
}
|
||||
} else {
|
||||
/* Ensure FUN_ARGS is at least cleared for consistency */
|
||||
setenv("FUN_ARGS", "", 1);
|
||||
}
|
||||
|
||||
Bytecode *bc = parse_file_to_bytecode(path);
|
||||
if (!bc) {
|
||||
fprintf(stderr, "Failed to compile script: %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vm_run(&vm, bc);
|
||||
vm_print_output(&vm);
|
||||
vm_clear_output(&vm);
|
||||
bytecode_free(bc);
|
||||
return vm.exit_code;
|
||||
/* Export FUN_ARGC */
|
||||
{
|
||||
char buf[32];
|
||||
snprintf(buf, sizeof(buf), "%d", sargc);
|
||||
setenv("FUN_ARGC", buf, 1);
|
||||
}
|
||||
|
||||
/* Export FUN_ARGV_i */
|
||||
for (int i = 0; i < sargc; ++i) {
|
||||
char key[32];
|
||||
snprintf(key, sizeof(key), "FUN_ARGV_%d", i);
|
||||
setenv(key, argv[sargi + i], 1);
|
||||
}
|
||||
|
||||
/* Optional: space-joined convenience string FUN_ARGS */
|
||||
if (sargc > 0) {
|
||||
size_t total = 0;
|
||||
for (int i = 0; i < sargc; ++i) {
|
||||
total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */
|
||||
}
|
||||
char *joined = (char *)malloc(total);
|
||||
if (joined) {
|
||||
joined[0] = '\0';
|
||||
for (int i = 0; i < sargc; ++i) {
|
||||
strcat(joined, argv[sargi + i]);
|
||||
if (i + 1 < sargc) strcat(joined, " ");
|
||||
}
|
||||
setenv("FUN_ARGS", joined, 1);
|
||||
free(joined);
|
||||
}
|
||||
} else {
|
||||
/* Ensure FUN_ARGS is at least cleared for consistency */
|
||||
setenv("FUN_ARGS", "", 1);
|
||||
}
|
||||
|
||||
Bytecode *bc = parse_file_to_bytecode(path);
|
||||
if (!bc) {
|
||||
fprintf(stderr, "Failed to compile script: %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vm_run(&vm, bc);
|
||||
vm_print_output(&vm);
|
||||
vm_clear_output(&vm);
|
||||
bytecode_free(bc);
|
||||
return vm.exit_code;
|
||||
}
|
||||
|
||||
#ifdef FUN_WITH_REPL
|
||||
return fun_run_repl(&vm);
|
||||
return fun_run_repl(&vm);
|
||||
#else
|
||||
fprintf(stderr, "Internal error: REPL not available in this build.\n");
|
||||
return 2;
|
||||
fprintf(stderr, "Internal error: REPL not available in this build.\n");
|
||||
return 2;
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
516
src/fun_test.c
516
src/fun_test.c
|
|
@ -10,302 +10,302 @@
|
|||
#include "bytecode.h"
|
||||
#include "value.h"
|
||||
#include "vm.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define ASSERT_EQ(val, expected) \
|
||||
if ((val).type != VAL_INT || (val).i != (expected)) { \
|
||||
fprintf(stderr, "Assertion failed: expected %lld, got ", (long long)(expected)); \
|
||||
print_value(&(val)); \
|
||||
printf("\n"); \
|
||||
return 1; \
|
||||
}
|
||||
#define ASSERT_EQ(val, expected) \
|
||||
if ((val).type != VAL_INT || (val).i != (expected)) { \
|
||||
fprintf(stderr, "Assertion failed: expected %lld, got ", (long long)(expected)); \
|
||||
print_value(&(val)); \
|
||||
printf("\n"); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
VM vm;
|
||||
vm_init(&vm);
|
||||
VM vm;
|
||||
vm_init(&vm);
|
||||
|
||||
Bytecode *bc = bytecode_new();
|
||||
Bytecode *bc = bytecode_new();
|
||||
|
||||
// constants
|
||||
int c0 = bytecode_add_constant(bc, make_int(0));
|
||||
int c1 = bytecode_add_constant(bc, make_int(1));
|
||||
int c2 = bytecode_add_constant(bc, make_int(2));
|
||||
int c3 = bytecode_add_constant(bc, make_int(3));
|
||||
int c10 = bytecode_add_constant(bc, make_int(10));
|
||||
int c42 = bytecode_add_constant(bc, make_int(42));
|
||||
int cf3_2 = bytecode_add_constant(bc, make_float(3.2));
|
||||
int cf3_5 = bytecode_add_constant(bc, make_float(3.5));
|
||||
int cf3_8 = bytecode_add_constant(bc, make_float(3.8));
|
||||
int cfn3_2 = bytecode_add_constant(bc, make_float(-3.2));
|
||||
int cfn3_5 = bytecode_add_constant(bc, make_float(-3.5));
|
||||
int cfn3_8 = bytecode_add_constant(bc, make_float(-3.8));
|
||||
int cf0 = bytecode_add_constant(bc, make_float(0.0));
|
||||
int cf1 = bytecode_add_constant(bc, make_float(1.0));
|
||||
int cf5 = bytecode_add_constant(bc, make_float(5.0));
|
||||
int c4 = bytecode_add_constant(bc, make_int(4));
|
||||
int c9 = bytecode_add_constant(bc, make_int(9));
|
||||
int c48 = bytecode_add_constant(bc, make_int(48));
|
||||
int c18 = bytecode_add_constant(bc, make_int(18));
|
||||
int c21 = bytecode_add_constant(bc, make_int(21));
|
||||
int c6 = bytecode_add_constant(bc, make_int(6));
|
||||
int c15 = bytecode_add_constant(bc, make_int(15));
|
||||
int c16 = bytecode_add_constant(bc, make_int(16));
|
||||
int cneg5 = bytecode_add_constant(bc, make_int(-5));
|
||||
int c7 = bytecode_add_constant(bc, make_int(7));
|
||||
// constants
|
||||
int c0 = bytecode_add_constant(bc, make_int(0));
|
||||
int c1 = bytecode_add_constant(bc, make_int(1));
|
||||
int c2 = bytecode_add_constant(bc, make_int(2));
|
||||
int c3 = bytecode_add_constant(bc, make_int(3));
|
||||
int c10 = bytecode_add_constant(bc, make_int(10));
|
||||
int c42 = bytecode_add_constant(bc, make_int(42));
|
||||
int cf3_2 = bytecode_add_constant(bc, make_float(3.2));
|
||||
int cf3_5 = bytecode_add_constant(bc, make_float(3.5));
|
||||
int cf3_8 = bytecode_add_constant(bc, make_float(3.8));
|
||||
int cfn3_2 = bytecode_add_constant(bc, make_float(-3.2));
|
||||
int cfn3_5 = bytecode_add_constant(bc, make_float(-3.5));
|
||||
int cfn3_8 = bytecode_add_constant(bc, make_float(-3.8));
|
||||
int cf0 = bytecode_add_constant(bc, make_float(0.0));
|
||||
int cf1 = bytecode_add_constant(bc, make_float(1.0));
|
||||
int cf5 = bytecode_add_constant(bc, make_float(5.0));
|
||||
int c4 = bytecode_add_constant(bc, make_int(4));
|
||||
int c9 = bytecode_add_constant(bc, make_int(9));
|
||||
int c48 = bytecode_add_constant(bc, make_int(48));
|
||||
int c18 = bytecode_add_constant(bc, make_int(18));
|
||||
int c21 = bytecode_add_constant(bc, make_int(21));
|
||||
int c6 = bytecode_add_constant(bc, make_int(6));
|
||||
int c15 = bytecode_add_constant(bc, make_int(15));
|
||||
int c16 = bytecode_add_constant(bc, make_int(16));
|
||||
int cneg5 = bytecode_add_constant(bc, make_int(-5));
|
||||
int c7 = bytecode_add_constant(bc, make_int(7));
|
||||
|
||||
// ---------- Arithmetic ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c42);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_ADD, 0); // 42+1=43
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// ---------- Arithmetic ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c42);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_ADD, 0); // 42+1=43
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_SUB, 0); // 10-3=7
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_SUB, 0); // 10-3=7
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_MUL, 0); // 2*3=6
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_MUL, 0); // 2*3=6
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_DIV, 0); // 10/2=5
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_DIV, 0); // 10/2=5
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_MOD, 0); // 10%3=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_MOD, 0); // 10%3=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// ---------- Comparisons ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LT, 0); // 1<2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// ---------- Comparisons ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LT, 0); // 1<2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LTE, 0); // 2<=2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LTE, 0); // 2<=2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_GT, 0); // 3>2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_GT, 0); // 3>2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_GTE, 0); // 2>=2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_GTE, 0); // 2>=2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_EQ, 0); // 2==2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_EQ, 0); // 2==2=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_NEQ, 0); // 2!=3=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c3);
|
||||
bytecode_add_instruction(bc, OP_NEQ, 0); // 2!=3=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// ---------- Logical ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_AND, 0); // 1&&0=0
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// ---------- Logical ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_AND, 0); // 1&&0=0
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_OR, 0); // 1||0=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_OR, 0); // 1||0=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_NOT, 0); // !0=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_NOT, 0); // !0=1
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// ---------- Stack ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_DUP, 0); // duplicate 1
|
||||
bytecode_add_instruction(bc, OP_ADD, 0); // 1+1=2
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// ---------- Stack ----------
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_DUP, 0); // duplicate 1
|
||||
bytecode_add_instruction(bc, OP_ADD, 0); // 1+1=2
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_SWAP, 0); // swap top two
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0); // top=1
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_SWAP, 0); // swap top two
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0); // top=1
|
||||
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_POP, 0); // dApache-2.0ard 1 (stack now empty)
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_POP, 0); // dApache-2.0ard 1 (stack now empty)
|
||||
|
||||
// ---------- Rounding (math.h) demo ----------
|
||||
// floor/ceil/trunc/round on representative values
|
||||
int start_round_demo = bc->instr_count;
|
||||
(void)start_round_demo;
|
||||
// ---------- Rounding (math.h) demo ----------
|
||||
// floor/ceil/trunc/round on representative values
|
||||
int start_round_demo = bc->instr_count;
|
||||
(void)start_round_demo;
|
||||
|
||||
// +3.2
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_FLOOR, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_CEIL, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_TRUNC, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_ROUND, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// +3.2
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_FLOOR, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_CEIL, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_TRUNC, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_ROUND, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// +3.5
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_5);
|
||||
bytecode_add_instruction(bc, OP_ROUND, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// +3.5
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_5);
|
||||
bytecode_add_instruction(bc, OP_ROUND, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// -3.5
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_5);
|
||||
bytecode_add_instruction(bc, OP_ROUND, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// -3.5
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_5);
|
||||
bytecode_add_instruction(bc, OP_ROUND, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// -3.2
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
|
||||
bytecode_add_instruction(bc, OP_FLOOR, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
|
||||
bytecode_add_instruction(bc, OP_CEIL, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// -3.2
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
|
||||
bytecode_add_instruction(bc, OP_FLOOR, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cfn3_2);
|
||||
bytecode_add_instruction(bc, OP_CEIL, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// integers should be unchanged
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_FLOOR, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// integers should be unchanged
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c10);
|
||||
bytecode_add_instruction(bc, OP_FLOOR, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// ---------- Transcendentals demo ----------
|
||||
// sin(0)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_SIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// cos(0)=1
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_COS, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// tan(0)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_TAN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// exp(0)=1
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_EXP, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// log(1)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
|
||||
bytecode_add_instruction(bc, OP_LOG, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// log10(1)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
|
||||
bytecode_add_instruction(bc, OP_LOG10, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// sqrt(9)=3
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c9);
|
||||
bytecode_add_instruction(bc, OP_SQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// ---------- Transcendentals demo ----------
|
||||
// sin(0)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_SIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// cos(0)=1
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_COS, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// tan(0)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_TAN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// exp(0)=1
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf0);
|
||||
bytecode_add_instruction(bc, OP_EXP, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// log(1)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
|
||||
bytecode_add_instruction(bc, OP_LOG, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// log10(1)=0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf1);
|
||||
bytecode_add_instruction(bc, OP_LOG10, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// sqrt(9)=3
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c9);
|
||||
bytecode_add_instruction(bc, OP_SQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// ---------- Integer math (gcd/lcm/isqrt/sign) demo ----------
|
||||
// gcd(48,18)=6
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c48);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c18);
|
||||
bytecode_add_instruction(bc, OP_GCD, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// ---------- Integer math (gcd/lcm/isqrt/sign) demo ----------
|
||||
// gcd(48,18)=6
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c48);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c18);
|
||||
bytecode_add_instruction(bc, OP_GCD, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// lcm(21,6)=42
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c21);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c6);
|
||||
bytecode_add_instruction(bc, OP_LCM, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// lcm(21,6)=42
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c21);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c6);
|
||||
bytecode_add_instruction(bc, OP_LCM, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// isqrt cases: 0, 1, 15->3, 16->4
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c15);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c16);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// isqrt cases: 0, 1, 15->3, 16->4
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c15);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c16);
|
||||
bytecode_add_instruction(bc, OP_ISQRT, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// sign(-5)=-1, sign(0)=0, sign(7)=1
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cneg5);
|
||||
bytecode_add_instruction(bc, OP_SIGN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_SIGN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c7);
|
||||
bytecode_add_instruction(bc, OP_SIGN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// sign(-5)=-1, sign(0)=0, sign(7)=1
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cneg5);
|
||||
bytecode_add_instruction(bc, OP_SIGN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
bytecode_add_instruction(bc, OP_SIGN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c7);
|
||||
bytecode_add_instruction(bc, OP_SIGN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// ---------- fmin/fmax demo ----------
|
||||
// fmin(3.2, 4) -> 3.2
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmax(3.2, 4) -> 4
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
|
||||
bytecode_add_instruction(bc, OP_FMAX, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// NaN cases
|
||||
double nanv = NAN;
|
||||
int cNaN = bytecode_add_constant(bc, make_float(nanv));
|
||||
// fmin(NaN, 5.0) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmax(NaN, 5.0) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_FMAX, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmin(5.0, NaN) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmax(5.0, NaN) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_FMAX, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmin(NaN, NaN) -> NaN (prints as nan)
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// ---------- fmin/fmax demo ----------
|
||||
// fmin(3.2, 4) -> 3.2
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmax(3.2, 4) -> 4
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf3_2);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c4);
|
||||
bytecode_add_instruction(bc, OP_FMAX, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// NaN cases
|
||||
double nanv = NAN;
|
||||
int cNaN = bytecode_add_constant(bc, make_float(nanv));
|
||||
// fmin(NaN, 5.0) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmax(NaN, 5.0) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_FMAX, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmin(5.0, NaN) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmax(5.0, NaN) -> 5.0
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cf5);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_FMAX, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
// fmin(NaN, NaN) -> NaN (prints as nan)
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, cNaN);
|
||||
bytecode_add_instruction(bc, OP_FMIN, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
// ---------- HALT ----------
|
||||
bytecode_add_instruction(bc, OP_HALT, 0);
|
||||
// ---------- HALT ----------
|
||||
bytecode_add_instruction(bc, OP_HALT, 0);
|
||||
|
||||
printf("=== Bytecode dump ===\n");
|
||||
for (int i = 0; i < bc->instr_count; ++i) {
|
||||
Instruction instr = bc->instructions[i];
|
||||
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
|
||||
}
|
||||
printf("=====================\n");
|
||||
printf("=== Bytecode dump ===\n");
|
||||
for (int i = 0; i < bc->instr_count; ++i) {
|
||||
Instruction instr = bc->instructions[i];
|
||||
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
|
||||
}
|
||||
printf("=====================\n");
|
||||
|
||||
// run VM
|
||||
vm_run(&vm, bc);
|
||||
// run VM
|
||||
vm_run(&vm, bc);
|
||||
|
||||
printf("All tests executed. Output count: %d\n", vm.output_count);
|
||||
printf("All tests executed. Output count: %d\n", vm.output_count);
|
||||
|
||||
vm_clear_output(&vm);
|
||||
bytecode_free(bc);
|
||||
return 0;
|
||||
vm_clear_output(&vm);
|
||||
bytecode_free(bc);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
84
src/iter.c
84
src/iter.c
|
|
@ -12,49 +12,51 @@
|
|||
|
||||
/* enumerate(arr) -> [[0, v0], [1, v1], ...] */
|
||||
Value bi_enumerate(const Value *arr) {
|
||||
int n = array_length(arr);
|
||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *pairs = (Value*)malloc(sizeof(Value) * n);
|
||||
if (!pairs) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value elem;
|
||||
array_get_copy(arr, i, &elem);
|
||||
Value kv_vals[2];
|
||||
kv_vals[0] = make_int(i);
|
||||
kv_vals[1] = elem;
|
||||
Value kv = make_array_from_values(kv_vals, 2);
|
||||
free_value(kv_vals[0]);
|
||||
free_value(kv_vals[1]);
|
||||
pairs[i] = kv;
|
||||
}
|
||||
Value out = make_array_from_values(pairs, n);
|
||||
for (int i = 0; i < n; ++i) free_value(pairs[i]);
|
||||
free(pairs);
|
||||
return out;
|
||||
int n = array_length(arr);
|
||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *pairs = (Value *)malloc(sizeof(Value) * n);
|
||||
if (!pairs) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value elem;
|
||||
array_get_copy(arr, i, &elem);
|
||||
Value kv_vals[2];
|
||||
kv_vals[0] = make_int(i);
|
||||
kv_vals[1] = elem;
|
||||
Value kv = make_array_from_values(kv_vals, 2);
|
||||
free_value(kv_vals[0]);
|
||||
free_value(kv_vals[1]);
|
||||
pairs[i] = kv;
|
||||
}
|
||||
Value out = make_array_from_values(pairs, n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
free_value(pairs[i]);
|
||||
free(pairs);
|
||||
return out;
|
||||
}
|
||||
|
||||
/* zip(a, b) -> [[a0,b0], [a1,b1], ...] up to min(len(a),len(b)) */
|
||||
Value bi_zip(const Value *a, const Value *b) {
|
||||
int na = array_length(a);
|
||||
int nb = array_length(b);
|
||||
int n = na < nb ? na : nb;
|
||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *pairs = (Value*)malloc(sizeof(Value) * n);
|
||||
if (!pairs) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value av, bv;
|
||||
array_get_copy(a, i, &av);
|
||||
array_get_copy(b, i, &bv);
|
||||
Value kv_vals[2];
|
||||
kv_vals[0] = av;
|
||||
kv_vals[1] = bv;
|
||||
Value kv = make_array_from_values(kv_vals, 2);
|
||||
free_value(kv_vals[0]);
|
||||
free_value(kv_vals[1]);
|
||||
pairs[i] = kv;
|
||||
}
|
||||
Value out = make_array_from_values(pairs, n);
|
||||
for (int i = 0; i < n; ++i) free_value(pairs[i]);
|
||||
free(pairs);
|
||||
return out;
|
||||
int na = array_length(a);
|
||||
int nb = array_length(b);
|
||||
int n = na < nb ? na : nb;
|
||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *pairs = (Value *)malloc(sizeof(Value) * n);
|
||||
if (!pairs) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value av, bv;
|
||||
array_get_copy(a, i, &av);
|
||||
array_get_copy(b, i, &bv);
|
||||
Value kv_vals[2];
|
||||
kv_vals[0] = av;
|
||||
kv_vals[1] = bv;
|
||||
Value kv = make_array_from_values(kv_vals, 2);
|
||||
free_value(kv_vals[0]);
|
||||
free_value(kv_vals[1]);
|
||||
pairs[i] = kv;
|
||||
}
|
||||
Value out = make_array_from_values(pairs, n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
free_value(pairs[i]);
|
||||
free(pairs);
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
163
src/map.c
163
src/map.c
|
|
@ -13,104 +13,113 @@
|
|||
|
||||
/* Internal Map definition; Value holds struct Map* */
|
||||
typedef struct Map {
|
||||
int refcount;
|
||||
int count;
|
||||
int cap;
|
||||
char **keys; /* each key owned here */
|
||||
Value *vals; /* each value owned here */
|
||||
int refcount;
|
||||
int count;
|
||||
int cap;
|
||||
char **keys; /* each key owned here */
|
||||
Value *vals; /* each value owned here */
|
||||
} Map;
|
||||
|
||||
Value make_map_empty(void) {
|
||||
Map *m = (Map*)malloc(sizeof(Map));
|
||||
if (!m) return make_nil();
|
||||
m->refcount = 1;
|
||||
m->count = 0;
|
||||
m->cap = 0;
|
||||
m->keys = NULL;
|
||||
m->vals = NULL;
|
||||
Value v;
|
||||
v.type = VAL_MAP;
|
||||
v.map = (struct Map*)m;
|
||||
return v;
|
||||
Map *m = (Map *)malloc(sizeof(Map));
|
||||
if (!m) return make_nil();
|
||||
m->refcount = 1;
|
||||
m->count = 0;
|
||||
m->cap = 0;
|
||||
m->keys = NULL;
|
||||
m->vals = NULL;
|
||||
Value v;
|
||||
v.type = VAL_MAP;
|
||||
v.map = (struct Map *)m;
|
||||
return v;
|
||||
}
|
||||
|
||||
static int map_ensure_cap(Map *m, int need) {
|
||||
if (m->cap >= need) return 1;
|
||||
int ncap = m->cap == 0 ? 4 : m->cap * 2;
|
||||
while (ncap < need) ncap *= 2;
|
||||
char **nkeys = (char**)realloc(m->keys, sizeof(char*) * ncap);
|
||||
Value *nvals = (Value*)realloc(m->vals, sizeof(Value) * ncap);
|
||||
if (!nkeys || !nvals) return 0;
|
||||
m->keys = nkeys;
|
||||
m->vals = nvals;
|
||||
m->cap = ncap;
|
||||
return 1;
|
||||
if (m->cap >= need) return 1;
|
||||
int ncap = m->cap == 0 ? 4 : m->cap * 2;
|
||||
while (ncap < need)
|
||||
ncap *= 2;
|
||||
char **nkeys = (char **)realloc(m->keys, sizeof(char *) * ncap);
|
||||
Value *nvals = (Value *)realloc(m->vals, sizeof(Value) * ncap);
|
||||
if (!nkeys || !nvals) return 0;
|
||||
m->keys = nkeys;
|
||||
m->vals = nvals;
|
||||
m->cap = ncap;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int map_set(Value *vm, const char *key, Value v) {
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) { free_value(v); return 0; }
|
||||
Map *m = (Map*)vm->map;
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (strcmp(m->keys[i], key) == 0) {
|
||||
free_value(m->vals[i]);
|
||||
m->vals[i] = v;
|
||||
return 1;
|
||||
}
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) {
|
||||
free_value(v);
|
||||
return 0;
|
||||
}
|
||||
Map *m = (Map *)vm->map;
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (strcmp(m->keys[i], key) == 0) {
|
||||
free_value(m->vals[i]);
|
||||
m->vals[i] = v;
|
||||
return 1;
|
||||
}
|
||||
if (!map_ensure_cap(m, m->count + 1)) { free_value(v); return 0; }
|
||||
m->keys[m->count] = strdup(key);
|
||||
m->vals[m->count] = v;
|
||||
m->count++;
|
||||
return 1;
|
||||
}
|
||||
if (!map_ensure_cap(m, m->count + 1)) {
|
||||
free_value(v);
|
||||
return 0;
|
||||
}
|
||||
m->keys[m->count] = strdup(key);
|
||||
m->vals[m->count] = v;
|
||||
m->count++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int map_get_copy(const Value *vm, const char *key, Value *out) {
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
||||
Map *m = (Map*)vm->map;
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (strcmp(m->keys[i], key) == 0) {
|
||||
if (out) *out = copy_value(&m->vals[i]);
|
||||
return 1;
|
||||
}
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
||||
Map *m = (Map *)vm->map;
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (strcmp(m->keys[i], key) == 0) {
|
||||
if (out) *out = copy_value(&m->vals[i]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int map_has(const Value *vm, const char *key) {
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
||||
Map *m = (Map*)vm->map;
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (strcmp(m->keys[i], key) == 0) return 1;
|
||||
}
|
||||
return 0;
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
||||
Map *m = (Map *)vm->map;
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (strcmp(m->keys[i], key) == 0) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Value map_keys_array(const Value *vm) {
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
||||
Map *m = (Map*)vm->map;
|
||||
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value*)malloc(sizeof(Value) * m->count);
|
||||
if (!tmp) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
tmp[i] = make_string(m->keys[i]);
|
||||
}
|
||||
Value arr = make_array_from_values(tmp, m->count);
|
||||
for (int i = 0; i < m->count; ++i) free_value(tmp[i]);
|
||||
free(tmp);
|
||||
return arr;
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
||||
Map *m = (Map *)vm->map;
|
||||
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
|
||||
if (!tmp) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
tmp[i] = make_string(m->keys[i]);
|
||||
}
|
||||
Value arr = make_array_from_values(tmp, m->count);
|
||||
for (int i = 0; i < m->count; ++i)
|
||||
free_value(tmp[i]);
|
||||
free(tmp);
|
||||
return arr;
|
||||
}
|
||||
|
||||
Value map_values_array(const Value *vm) {
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
||||
Map *m = (Map*)vm->map;
|
||||
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value*)malloc(sizeof(Value) * m->count);
|
||||
if (!tmp) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
tmp[i] = copy_value(&m->vals[i]);
|
||||
}
|
||||
Value arr = make_array_from_values(tmp, m->count);
|
||||
for (int i = 0; i < m->count; ++i) free_value(tmp[i]);
|
||||
free(tmp);
|
||||
return arr;
|
||||
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
||||
Map *m = (Map *)vm->map;
|
||||
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
|
||||
if (!tmp) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
tmp[i] = copy_value(&m->vals[i]);
|
||||
}
|
||||
Value arr = make_array_from_values(tmp, m->count);
|
||||
for (int i = 0; i < m->count; ++i)
|
||||
free_value(tmp[i]);
|
||||
free(tmp);
|
||||
return arr;
|
||||
}
|
||||
|
|
|
|||
11547
src/parser.c
11547
src/parser.c
File diff suppressed because it is too large
Load diff
1366
src/parser_utils.c
1366
src/parser_utils.c
File diff suppressed because it is too large
Load diff
3482
src/repl.c
3482
src/repl.c
File diff suppressed because it is too large
Load diff
207
src/str_utils.c
207
src/str_utils.c
|
|
@ -14,121 +14,126 @@
|
|||
/* string helpers returning newly allocated C strings or arrays */
|
||||
|
||||
char *string_substr(const char *s, int start, int len) {
|
||||
if (!s) return strdup("");
|
||||
int n = (int)strlen(s);
|
||||
if (start < 0) start = 0;
|
||||
if (start > n) start = n;
|
||||
if (len < 0) len = 0;
|
||||
if (start + len > n) len = n - start;
|
||||
char *out = (char*)malloc((size_t)len + 1);
|
||||
if (!out) return strdup("");
|
||||
memcpy(out, s + start, (size_t)len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
if (!s) return strdup("");
|
||||
int n = (int)strlen(s);
|
||||
if (start < 0) start = 0;
|
||||
if (start > n) start = n;
|
||||
if (len < 0) len = 0;
|
||||
if (start + len > n) len = n - start;
|
||||
char *out = (char *)malloc((size_t)len + 1);
|
||||
if (!out) return strdup("");
|
||||
memcpy(out, s + start, (size_t)len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
int string_find(const char *hay, const char *needle) {
|
||||
if (!hay || !needle) return -1;
|
||||
const char *p = strstr(hay, needle);
|
||||
if (!p) return -1;
|
||||
return (int)(p - hay);
|
||||
if (!hay || !needle) return -1;
|
||||
const char *p = strstr(hay, needle);
|
||||
if (!p) return -1;
|
||||
return (int)(p - hay);
|
||||
}
|
||||
|
||||
Value string_split_to_array(const char *s, const char *sep) {
|
||||
if (!s) s = "";
|
||||
if (!sep) sep = "";
|
||||
int seplen = (int)strlen(sep);
|
||||
if (seplen == 0) {
|
||||
/* split into characters */
|
||||
int n = (int)strlen(s);
|
||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value*)malloc(sizeof(Value) * n);
|
||||
if (!tmp) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
char ch[2] = { s[i], 0 };
|
||||
tmp[i] = make_string(ch);
|
||||
}
|
||||
Value arr = make_array_from_values(tmp, n);
|
||||
for (int i = 0; i < n; ++i) free_value(tmp[i]);
|
||||
free(tmp);
|
||||
return arr;
|
||||
if (!s) s = "";
|
||||
if (!sep) sep = "";
|
||||
int seplen = (int)strlen(sep);
|
||||
if (seplen == 0) {
|
||||
/* split into characters */
|
||||
int n = (int)strlen(s);
|
||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value *)malloc(sizeof(Value) * n);
|
||||
if (!tmp) return make_array_from_values(NULL, 0);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
char ch[2] = {s[i], 0};
|
||||
tmp[i] = make_string(ch);
|
||||
}
|
||||
/* split by separator */
|
||||
Value *parts = NULL;
|
||||
int count = 0;
|
||||
int cap = 0;
|
||||
const char *cur = s;
|
||||
const char *pos = NULL;
|
||||
while ((pos = strstr(cur, sep)) != NULL) {
|
||||
int len = (int)(pos - cur);
|
||||
char *piece = (char*)malloc((size_t)len + 1);
|
||||
if (!piece) break;
|
||||
memcpy(piece, cur, (size_t)len);
|
||||
piece[len] = '\0';
|
||||
if (count >= cap) {
|
||||
cap = cap == 0 ? 4 : cap * 2;
|
||||
parts = (Value*)realloc(parts, sizeof(Value) * cap);
|
||||
}
|
||||
parts[count++] = make_string(piece);
|
||||
free(piece);
|
||||
cur = pos + seplen;
|
||||
}
|
||||
/* tail */
|
||||
char *tail = strdup(cur ? cur : "");
|
||||
if (count >= cap) {
|
||||
cap = cap == 0 ? 1 : cap + 1;
|
||||
parts = (Value*)realloc(parts, sizeof(Value) * cap);
|
||||
}
|
||||
parts[count++] = make_string(tail ? tail : "");
|
||||
free(tail);
|
||||
|
||||
Value arr = make_array_from_values(parts, count);
|
||||
for (int i = 0; i < count; ++i) free_value(parts[i]);
|
||||
free(parts);
|
||||
Value arr = make_array_from_values(tmp, n);
|
||||
for (int i = 0; i < n; ++i)
|
||||
free_value(tmp[i]);
|
||||
free(tmp);
|
||||
return arr;
|
||||
}
|
||||
/* split by separator */
|
||||
Value *parts = NULL;
|
||||
int count = 0;
|
||||
int cap = 0;
|
||||
const char *cur = s;
|
||||
const char *pos = NULL;
|
||||
while ((pos = strstr(cur, sep)) != NULL) {
|
||||
int len = (int)(pos - cur);
|
||||
char *piece = (char *)malloc((size_t)len + 1);
|
||||
if (!piece) break;
|
||||
memcpy(piece, cur, (size_t)len);
|
||||
piece[len] = '\0';
|
||||
if (count >= cap) {
|
||||
cap = cap == 0 ? 4 : cap * 2;
|
||||
parts = (Value *)realloc(parts, sizeof(Value) * cap);
|
||||
}
|
||||
parts[count++] = make_string(piece);
|
||||
free(piece);
|
||||
cur = pos + seplen;
|
||||
}
|
||||
/* tail */
|
||||
char *tail = strdup(cur ? cur : "");
|
||||
if (count >= cap) {
|
||||
cap = cap == 0 ? 1 : cap + 1;
|
||||
parts = (Value *)realloc(parts, sizeof(Value) * cap);
|
||||
}
|
||||
parts[count++] = make_string(tail ? tail : "");
|
||||
free(tail);
|
||||
|
||||
Value arr = make_array_from_values(parts, count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
free_value(parts[i]);
|
||||
free(parts);
|
||||
return arr;
|
||||
}
|
||||
|
||||
char *array_join_with_sep(const Value *v, const char *sep) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return strdup("");
|
||||
if (!sep) sep = "";
|
||||
/* Array is defined in value.c; we only need safe public access. */
|
||||
const int n = array_length(v);
|
||||
if (n <= 0) return strdup("");
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return strdup("");
|
||||
if (!sep) sep = "";
|
||||
/* Array is defined in value.c; we only need safe public access. */
|
||||
const int n = array_length(v);
|
||||
if (n <= 0) return strdup("");
|
||||
|
||||
char **parts = (char**)malloc(sizeof(char*) * n);
|
||||
if (!parts) return strdup("");
|
||||
char **parts = (char **)malloc(sizeof(char *) * n);
|
||||
if (!parts) return strdup("");
|
||||
|
||||
size_t total = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (!array_get_copy(v, i, &item)) {
|
||||
parts[i] = strdup("");
|
||||
} else {
|
||||
parts[i] = value_to_string_alloc(&item);
|
||||
free_value(item);
|
||||
}
|
||||
total += strlen(parts[i]);
|
||||
if (i + 1 < n) total += strlen(sep);
|
||||
size_t total = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Value item;
|
||||
if (!array_get_copy(v, i, &item)) {
|
||||
parts[i] = strdup("");
|
||||
} else {
|
||||
parts[i] = value_to_string_alloc(&item);
|
||||
free_value(item);
|
||||
}
|
||||
total += strlen(parts[i]);
|
||||
if (i + 1 < n) total += strlen(sep);
|
||||
}
|
||||
|
||||
char *out = (char*)malloc(total + 1);
|
||||
if (!out) {
|
||||
for (int i = 0; i < n; ++i) free(parts[i]);
|
||||
free(parts);
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
size_t off = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
size_t li = strlen(parts[i]);
|
||||
memcpy(out + off, parts[i], li); off += li;
|
||||
if (i + 1 < n) {
|
||||
size_t ls = strlen(sep);
|
||||
memcpy(out + off, sep, ls); off += ls;
|
||||
}
|
||||
free(parts[i]);
|
||||
}
|
||||
char *out = (char *)malloc(total + 1);
|
||||
if (!out) {
|
||||
for (int i = 0; i < n; ++i)
|
||||
free(parts[i]);
|
||||
free(parts);
|
||||
out[off] = '\0';
|
||||
return out;
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
size_t off = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
size_t li = strlen(parts[i]);
|
||||
memcpy(out + off, parts[i], li);
|
||||
off += li;
|
||||
if (i + 1 < n) {
|
||||
size_t ls = strlen(sep);
|
||||
memcpy(out + off, sep, ls);
|
||||
off += ls;
|
||||
}
|
||||
free(parts[i]);
|
||||
}
|
||||
free(parts);
|
||||
out[off] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
|
|
|||
32
src/string.c
32
src/string.c
|
|
@ -13,29 +13,29 @@
|
|||
/* String built-ins wrappers used by VM opcodes */
|
||||
|
||||
Value bi_split(const Value *str, const Value *sep) {
|
||||
const char *s = (str && str->type == VAL_STRING && str->s) ? str->s : "";
|
||||
const char *p = (sep && sep->type == VAL_STRING && sep->s) ? sep->s : "";
|
||||
return string_split_to_array(s, p);
|
||||
const char *s = (str && str->type == VAL_STRING && str->s) ? str->s : "";
|
||||
const char *p = (sep && sep->type == VAL_STRING && sep->s) ? sep->s : "";
|
||||
return string_split_to_array(s, p);
|
||||
}
|
||||
|
||||
Value bi_join(const Value *arr, const Value *sep) {
|
||||
const char *p = (sep && sep->type == VAL_STRING && sep->s) ? sep->s : "";
|
||||
char *s = array_join_with_sep(arr, p);
|
||||
Value out = make_string(s ? s : "");
|
||||
if (s) free(s);
|
||||
return out;
|
||||
const char *p = (sep && sep->type == VAL_STRING && sep->s) ? sep->s : "";
|
||||
char *s = array_join_with_sep(arr, p);
|
||||
Value out = make_string(s ? s : "");
|
||||
if (s) free(s);
|
||||
return out;
|
||||
}
|
||||
|
||||
Value bi_substr(const Value *str, int start, int len) {
|
||||
const char *s = (str && str->type == VAL_STRING && str->s) ? str->s : "";
|
||||
char *sub = string_substr(s, start, len);
|
||||
Value out = make_string(sub ? sub : "");
|
||||
if (sub) free(sub);
|
||||
return out;
|
||||
const char *s = (str && str->type == VAL_STRING && str->s) ? str->s : "";
|
||||
char *sub = string_substr(s, start, len);
|
||||
Value out = make_string(sub ? sub : "");
|
||||
if (sub) free(sub);
|
||||
return out;
|
||||
}
|
||||
|
||||
int bi_find(const Value *hay, const Value *needle) {
|
||||
const char *h = (hay && hay->type == VAL_STRING && hay->s) ? hay->s : "";
|
||||
const char *n = (needle && needle->type == VAL_STRING && needle->s) ? needle->s : "";
|
||||
return string_find(h, n);
|
||||
const char *h = (hay && hay->type == VAL_STRING && hay->s) ? hay->s : "";
|
||||
const char *n = (needle && needle->type == VAL_STRING && needle->s) ? needle->s : "";
|
||||
return string_find(h, n);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,69 +7,69 @@
|
|||
* https://opensource.org/license/apache-2-0
|
||||
*/
|
||||
|
||||
#include "vm.h"
|
||||
#include "bytecode.h"
|
||||
#include "value.h"
|
||||
#include "vm.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
|
||||
VM vm;
|
||||
vm_init(&vm);
|
||||
|
||||
Bytecode *bc = bytecode_new();
|
||||
VM vm;
|
||||
vm_init(&vm);
|
||||
|
||||
// Example: test OP_ADD
|
||||
int c1 = bytecode_add_constant(bc, make_int(5));
|
||||
int c2 = bytecode_add_constant(bc, make_int(3));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_ADD, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
Bytecode *bc = bytecode_new();
|
||||
|
||||
printf("=== Bytecode dump ===\n");
|
||||
for (int i = 0; i < bc->instr_count; ++i) {
|
||||
Instruction instr = bc->instructions[i];
|
||||
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
|
||||
}
|
||||
printf("=====================\n");
|
||||
bytecode_dump(bc);
|
||||
printf("=====================\n");
|
||||
|
||||
vm_run(&vm, bc);
|
||||
// Example: test OP_ADD
|
||||
int c1 = bytecode_add_constant(bc, make_int(5));
|
||||
int c2 = bytecode_add_constant(bc, make_int(3));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c2);
|
||||
bytecode_add_instruction(bc, OP_ADD, 0);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
|
||||
printf("Output count: %d\n", vm.output_count);
|
||||
for (int i = 0; i < vm.output_count; i++) {
|
||||
printf("Output[%d] = ", i);
|
||||
print_value(&vm.output[i]);
|
||||
printf("\n");
|
||||
}
|
||||
printf("=== Bytecode dump ===\n");
|
||||
for (int i = 0; i < bc->instr_count; ++i) {
|
||||
Instruction instr = bc->instructions[i];
|
||||
printf("instr %3d: opcode=%2d operand=%d\n", i, instr.op, instr.operand);
|
||||
}
|
||||
printf("=====================\n");
|
||||
bytecode_dump(bc);
|
||||
printf("=====================\n");
|
||||
|
||||
vm_clear_output(&vm);
|
||||
vm_run(&vm, bc);
|
||||
|
||||
/* --- Rust FFI demo: call a Rust opcode and string function --- */
|
||||
printf("Output count: %d\n", vm.output_count);
|
||||
for (int i = 0; i < vm.output_count; i++) {
|
||||
printf("Output[%d] = ", i);
|
||||
print_value(&vm.output[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
vm_clear_output(&vm);
|
||||
|
||||
/* --- Rust FFI demo: call a Rust opcode and string function --- */
|
||||
#ifdef FUN_WITH_RUST
|
||||
extern int fun_op_radd(VM *vm);
|
||||
extern const char *fun_rust_get_string(void);
|
||||
extern int fun_op_radd(VM * vm);
|
||||
extern const char *fun_rust_get_string(void);
|
||||
|
||||
printf("=== Rust FFI demo ===\n");
|
||||
const char *rs = fun_rust_get_string();
|
||||
if (rs) {
|
||||
printf("Rust says: %s\n", rs);
|
||||
}
|
||||
printf("=== Rust FFI demo ===\n");
|
||||
const char *rs = fun_rust_get_string();
|
||||
if (rs) {
|
||||
printf("Rust says: %s\n", rs);
|
||||
}
|
||||
|
||||
/* prepare stack: push 10 and 32, then call Rust add -> expect 42 */
|
||||
vm_push_i64(&vm, 10);
|
||||
vm_push_i64(&vm, 32);
|
||||
int rc = fun_op_radd(&vm);
|
||||
printf("fun_op_radd rc=%d\n", rc);
|
||||
long long sum = (long long)vm_pop_i64(&vm);
|
||||
printf("Rust op result: %lld\n", sum);
|
||||
/* prepare stack: push 10 and 32, then call Rust add -> expect 42 */
|
||||
vm_push_i64(&vm, 10);
|
||||
vm_push_i64(&vm, 32);
|
||||
int rc = fun_op_radd(&vm);
|
||||
printf("fun_op_radd rc=%d\n", rc);
|
||||
long long sum = (long long)vm_pop_i64(&vm);
|
||||
printf("Rust op result: %lld\n", sum);
|
||||
#else
|
||||
printf("=== Rust FFI demo (disabled; build with -DFUN_WITH_RUST=ON) ===\n");
|
||||
printf("=== Rust FFI demo (disabled; build with -DFUN_WITH_RUST=ON) ===\n");
|
||||
#endif
|
||||
|
||||
vm_free(&vm);
|
||||
bytecode_free(bc);
|
||||
return 0;
|
||||
vm_free(&vm);
|
||||
bytecode_free(bc);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
767
src/value.c
767
src/value.c
|
|
@ -8,470 +8,487 @@
|
|||
*/
|
||||
|
||||
#include "value.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Compile helper implementations into this TU to avoid build system changes */
|
||||
#include "str_utils.c"
|
||||
#include "array_utils.c"
|
||||
#include "str_utils.c"
|
||||
|
||||
typedef struct Array {
|
||||
int refcount;
|
||||
int count;
|
||||
Value *items; /* owns items; each item owned by array */
|
||||
int refcount;
|
||||
int count;
|
||||
Value *items; /* owns items; each item owned by array */
|
||||
} Array;
|
||||
|
||||
typedef struct Map {
|
||||
int refcount;
|
||||
int count;
|
||||
int cap;
|
||||
char **keys; /* each key owned here */
|
||||
Value *vals; /* each value owned here */
|
||||
int refcount;
|
||||
int count;
|
||||
int cap;
|
||||
char **keys; /* each key owned here */
|
||||
Value *vals; /* each value owned here */
|
||||
} Map;
|
||||
|
||||
Value make_int(int64_t v) {
|
||||
Value val;
|
||||
val.type = VAL_INT;
|
||||
val.i = v;
|
||||
return val;
|
||||
Value val;
|
||||
val.type = VAL_INT;
|
||||
val.i = v;
|
||||
return val;
|
||||
}
|
||||
|
||||
Value make_float(double v) {
|
||||
Value val;
|
||||
val.type = VAL_FLOAT;
|
||||
val.d = v;
|
||||
return val;
|
||||
Value val;
|
||||
val.type = VAL_FLOAT;
|
||||
val.d = v;
|
||||
return val;
|
||||
}
|
||||
|
||||
Value make_bool(int v) {
|
||||
Value val;
|
||||
val.type = VAL_BOOL;
|
||||
val.i = v ? 1 : 0;
|
||||
return val;
|
||||
Value val;
|
||||
val.type = VAL_BOOL;
|
||||
val.i = v ? 1 : 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
Value make_string(const char *s) {
|
||||
Value val;
|
||||
val.type = VAL_STRING;
|
||||
if (s) val.s = strdup(s);
|
||||
else val.s = strdup("");
|
||||
return val;
|
||||
Value val;
|
||||
val.type = VAL_STRING;
|
||||
if (s)
|
||||
val.s = strdup(s);
|
||||
else
|
||||
val.s = strdup("");
|
||||
return val;
|
||||
}
|
||||
|
||||
Value make_function(struct Bytecode *fn) {
|
||||
Value val;
|
||||
val.type = VAL_FUNCTION;
|
||||
val.fn = fn;
|
||||
return val;
|
||||
Value val;
|
||||
val.type = VAL_FUNCTION;
|
||||
val.fn = fn;
|
||||
return val;
|
||||
}
|
||||
|
||||
Value make_nil(void) {
|
||||
Value v;
|
||||
v.type = VAL_NIL;
|
||||
return v;
|
||||
Value v;
|
||||
v.type = VAL_NIL;
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Value make_array_from_values(const Value *vals, int count) {
|
||||
if (count < 0) count = 0;
|
||||
Array *arr = (Array*)malloc(sizeof(Array));
|
||||
if (!arr) {
|
||||
Value nil = make_nil();
|
||||
return nil;
|
||||
if (count < 0) count = 0;
|
||||
Array *arr = (Array *)malloc(sizeof(Array));
|
||||
if (!arr) {
|
||||
Value nil = make_nil();
|
||||
return nil;
|
||||
}
|
||||
arr->refcount = 1;
|
||||
arr->count = count;
|
||||
if (count > 0) {
|
||||
arr->items = (Value *)malloc(sizeof(Value) * count);
|
||||
if (!arr->items) {
|
||||
free(arr);
|
||||
Value nil = make_nil();
|
||||
return nil;
|
||||
}
|
||||
arr->refcount = 1;
|
||||
arr->count = count;
|
||||
if (count > 0) {
|
||||
arr->items = (Value*)malloc(sizeof(Value) * count);
|
||||
if (!arr->items) {
|
||||
free(arr);
|
||||
Value nil = make_nil();
|
||||
return nil;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
arr->items[i] = copy_value(&vals[i]);
|
||||
}
|
||||
} else {
|
||||
arr->items = NULL;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
arr->items[i] = copy_value(&vals[i]);
|
||||
}
|
||||
Value v;
|
||||
v.type = VAL_ARRAY;
|
||||
v.arr = (struct Array*)arr;
|
||||
return v;
|
||||
} else {
|
||||
arr->items = NULL;
|
||||
}
|
||||
Value v;
|
||||
v.type = VAL_ARRAY;
|
||||
v.arr = (struct Array *)arr;
|
||||
return v;
|
||||
}
|
||||
|
||||
int array_length(const Value *v) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
const Array *a = (const Array*)v->arr;
|
||||
return a->count;
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
const Array *a = (const Array *)v->arr;
|
||||
return a->count;
|
||||
}
|
||||
|
||||
int array_get_copy(const Value *v, int index, Value *out) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
const Array *a = (const Array*)v->arr;
|
||||
if (index < 0 || index >= a->count) return 0;
|
||||
if (out) *out = copy_value(&a->items[index]);
|
||||
return 1;
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
const Array *a = (const Array *)v->arr;
|
||||
if (index < 0 || index >= a->count) return 0;
|
||||
if (out) *out = copy_value(&a->items[index]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int array_set(Value *v, int index, Value newElem) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
Array *a = (Array*)v->arr;
|
||||
if (index < 0 || index >= a->count) return 0;
|
||||
free_value(a->items[index]);
|
||||
a->items[index] = newElem; /* take ownership */
|
||||
return 1;
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
Array *a = (Array *)v->arr;
|
||||
if (index < 0 || index >= a->count) return 0;
|
||||
free_value(a->items[index]);
|
||||
a->items[index] = newElem; /* take ownership */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ensure_array_capacity(Array *a, int newCount) {
|
||||
if (newCount <= a->count) return 1;
|
||||
/* grow to at least newCount; double strategy */
|
||||
int curr = a->count;
|
||||
int cap = curr;
|
||||
if (cap < 4) cap = 4;
|
||||
while (cap < newCount) cap *= 2;
|
||||
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * cap);
|
||||
if (!newItems) return 0;
|
||||
/* if growing beyond current count, initialize new slots to nil */
|
||||
if (cap > a->count) {
|
||||
for (int i = a->count; i < cap; ++i) {
|
||||
newItems[i] = make_nil();
|
||||
}
|
||||
if (newCount <= a->count) return 1;
|
||||
/* grow to at least newCount; double strategy */
|
||||
int curr = a->count;
|
||||
int cap = curr;
|
||||
if (cap < 4) cap = 4;
|
||||
while (cap < newCount)
|
||||
cap *= 2;
|
||||
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * cap);
|
||||
if (!newItems) return 0;
|
||||
/* if growing beyond current count, initialize new slots to nil */
|
||||
if (cap > a->count) {
|
||||
for (int i = a->count; i < cap; ++i) {
|
||||
newItems[i] = make_nil();
|
||||
}
|
||||
a->items = newItems;
|
||||
return 1;
|
||||
}
|
||||
a->items = newItems;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int array_push(Value *v, Value newElem) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
Array *a = (Array*)v->arr;
|
||||
/* ensure capacity for count+1 by reallocating items array to at least count+1 elements */
|
||||
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1));
|
||||
if (!newItems) { free_value(newElem); return -1; }
|
||||
a->items = newItems;
|
||||
a->items[a->count] = newElem; /* take ownership */
|
||||
a->count += 1;
|
||||
return a->count;
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
Array *a = (Array *)v->arr;
|
||||
/* ensure capacity for count+1 by reallocating items array to at least count+1 elements */
|
||||
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
|
||||
if (!newItems) {
|
||||
free_value(newElem);
|
||||
return -1;
|
||||
}
|
||||
a->items = newItems;
|
||||
a->items[a->count] = newElem; /* take ownership */
|
||||
a->count += 1;
|
||||
return a->count;
|
||||
}
|
||||
|
||||
int array_pop(Value *v, Value *out) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
Array *a = (Array*)v->arr;
|
||||
if (a->count <= 0) return 0;
|
||||
int idx = a->count - 1;
|
||||
if (out) *out = a->items[idx]; /* transfer ownership */
|
||||
else free_value(a->items[idx]);
|
||||
a->count -= 1;
|
||||
return 1;
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
Array *a = (Array *)v->arr;
|
||||
if (a->count <= 0) return 0;
|
||||
int idx = a->count - 1;
|
||||
if (out)
|
||||
*out = a->items[idx]; /* transfer ownership */
|
||||
else
|
||||
free_value(a->items[idx]);
|
||||
a->count -= 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int array_insert(Value *v, int index, Value newElem) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
Array *a = (Array*)v->arr;
|
||||
if (index < 0) index = 0;
|
||||
if (index > a->count) index = a->count;
|
||||
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1));
|
||||
if (!newItems) { free_value(newElem); return -1; }
|
||||
a->items = newItems;
|
||||
/* shift right */
|
||||
for (int i = a->count; i > index; --i) {
|
||||
a->items[i] = a->items[i - 1];
|
||||
}
|
||||
a->items[index] = newElem; /* take ownership */
|
||||
a->count += 1;
|
||||
return a->count;
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||
Array *a = (Array *)v->arr;
|
||||
if (index < 0) index = 0;
|
||||
if (index > a->count) index = a->count;
|
||||
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
|
||||
if (!newItems) {
|
||||
free_value(newElem);
|
||||
return -1;
|
||||
}
|
||||
a->items = newItems;
|
||||
/* shift right */
|
||||
for (int i = a->count; i > index; --i) {
|
||||
a->items[i] = a->items[i - 1];
|
||||
}
|
||||
a->items[index] = newElem; /* take ownership */
|
||||
a->count += 1;
|
||||
return a->count;
|
||||
}
|
||||
|
||||
int array_remove(Value *v, int index, Value *out) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
Array *a = (Array*)v->arr;
|
||||
if (index < 0 || index >= a->count) return 0;
|
||||
if (out) *out = a->items[index]; /* transfer ownership */
|
||||
else free_value(a->items[index]);
|
||||
/* shift left */
|
||||
for (int i = index; i < a->count - 1; ++i) {
|
||||
a->items[i] = a->items[i + 1];
|
||||
}
|
||||
a->count -= 1;
|
||||
return 1;
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||
Array *a = (Array *)v->arr;
|
||||
if (index < 0 || index >= a->count) return 0;
|
||||
if (out)
|
||||
*out = a->items[index]; /* transfer ownership */
|
||||
else
|
||||
free_value(a->items[index]);
|
||||
/* shift left */
|
||||
for (int i = index; i < a->count - 1; ++i) {
|
||||
a->items[i] = a->items[i + 1];
|
||||
}
|
||||
a->count -= 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Value array_slice(const Value *v, int start, int end) {
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil();
|
||||
const Array *a = (const Array*)v->arr;
|
||||
int n = a->count;
|
||||
if (start < 0) start = 0;
|
||||
if (end < 0 || end > n) end = n;
|
||||
if (start > end) start = end;
|
||||
int m = end - start;
|
||||
if (m <= 0) {
|
||||
return make_array_from_values(NULL, 0);
|
||||
}
|
||||
return make_array_from_values(a->items + start, m);
|
||||
if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil();
|
||||
const Array *a = (const Array *)v->arr;
|
||||
int n = a->count;
|
||||
if (start < 0) start = 0;
|
||||
if (end < 0 || end > n) end = n;
|
||||
if (start > end) start = end;
|
||||
int m = end - start;
|
||||
if (m <= 0) {
|
||||
return make_array_from_values(NULL, 0);
|
||||
}
|
||||
return make_array_from_values(a->items + start, m);
|
||||
}
|
||||
|
||||
Value array_concat(const Value *av, const Value *bv) {
|
||||
if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil();
|
||||
const Array *a = (const Array*)av->arr;
|
||||
const Array *b = (const Array*)bv->arr;
|
||||
int na = a ? a->count : 0;
|
||||
int nb = b ? b->count : 0;
|
||||
int total = na + nb;
|
||||
if (total <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value*)malloc(sizeof(Value) * total);
|
||||
if (!tmp) return make_nil();
|
||||
for (int i = 0; i < na; ++i) tmp[i] = a->items[i];
|
||||
for (int j = 0; j < nb; ++j) tmp[na + j] = b->items[j];
|
||||
Value out = make_array_from_values(tmp, total);
|
||||
/* free temporaries we copied from (deep copy in make_array_from_values) */
|
||||
free(tmp);
|
||||
return out;
|
||||
if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil();
|
||||
const Array *a = (const Array *)av->arr;
|
||||
const Array *b = (const Array *)bv->arr;
|
||||
int na = a ? a->count : 0;
|
||||
int nb = b ? b->count : 0;
|
||||
int total = na + nb;
|
||||
if (total <= 0) return make_array_from_values(NULL, 0);
|
||||
Value *tmp = (Value *)malloc(sizeof(Value) * total);
|
||||
if (!tmp) return make_nil();
|
||||
for (int i = 0; i < na; ++i)
|
||||
tmp[i] = a->items[i];
|
||||
for (int j = 0; j < nb; ++j)
|
||||
tmp[na + j] = b->items[j];
|
||||
Value out = make_array_from_values(tmp, total);
|
||||
/* free temporaries we copied from (deep copy in make_array_from_values) */
|
||||
free(tmp);
|
||||
return out;
|
||||
}
|
||||
|
||||
Value copy_value(const Value *v) {
|
||||
Value out;
|
||||
out.type = v->type;
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
out.i = v->i;
|
||||
break;
|
||||
case VAL_FLOAT:
|
||||
out.d = v->d;
|
||||
break;
|
||||
case VAL_BOOL:
|
||||
out.i = v->i ? 1 : 0;
|
||||
break;
|
||||
case VAL_STRING:
|
||||
out.s = v->s ? strdup(v->s) : strdup("");
|
||||
break;
|
||||
case VAL_FUNCTION:
|
||||
out.fn = v->fn; /* shallow copy pointer */
|
||||
break;
|
||||
case VAL_ARRAY: {
|
||||
Array *a = (Array*)v->arr;
|
||||
out.arr = (struct Array*)a;
|
||||
if (a) a->refcount++;
|
||||
break;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
Map *m = (Map*)v->map;
|
||||
out.map = (struct Map*)m;
|
||||
if (m) m->refcount++;
|
||||
break;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
Value out;
|
||||
out.type = v->type;
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
out.i = v->i;
|
||||
break;
|
||||
case VAL_FLOAT:
|
||||
out.d = v->d;
|
||||
break;
|
||||
case VAL_BOOL:
|
||||
out.i = v->i ? 1 : 0;
|
||||
break;
|
||||
case VAL_STRING:
|
||||
out.s = v->s ? strdup(v->s) : strdup("");
|
||||
break;
|
||||
case VAL_FUNCTION:
|
||||
out.fn = v->fn; /* shallow copy pointer */
|
||||
break;
|
||||
case VAL_ARRAY: {
|
||||
Array *a = (Array *)v->arr;
|
||||
out.arr = (struct Array *)a;
|
||||
if (a) a->refcount++;
|
||||
break;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
Map *m = (Map *)v->map;
|
||||
out.map = (struct Map *)m;
|
||||
if (m) m->refcount++;
|
||||
break;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* deep copy including arrays (recursively copies items) */
|
||||
Value deep_copy_value(const Value *v) {
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return make_int(v->i);
|
||||
case VAL_FLOAT:
|
||||
return make_float(v->d);
|
||||
case VAL_BOOL:
|
||||
return make_bool(v->i);
|
||||
case VAL_STRING:
|
||||
return make_string(v->s ? v->s : "");
|
||||
case VAL_FUNCTION:
|
||||
return make_function(v->fn); /* shallow pointer for function bytecode */
|
||||
case VAL_ARRAY: {
|
||||
const Array *a = (const Array*)v->arr;
|
||||
if (!a || a->count <= 0) {
|
||||
return make_array_from_values(NULL, 0);
|
||||
}
|
||||
/* copy items deeply */
|
||||
Value *tmp = (Value*)malloc(sizeof(Value) * a->count);
|
||||
if (!tmp) return make_nil();
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
tmp[i] = deep_copy_value(&a->items[i]);
|
||||
}
|
||||
Value out = make_array_from_values(tmp, a->count);
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
free_value(tmp[i]);
|
||||
}
|
||||
free(tmp);
|
||||
return out;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
const Map *m = (const Map*)v->map;
|
||||
if (!m || m->count <= 0) return make_map_empty();
|
||||
Value out = make_map_empty();
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
Value dv = deep_copy_value(&m->vals[i]);
|
||||
map_set(&out, m->keys[i], dv);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
return make_nil();
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return make_int(v->i);
|
||||
case VAL_FLOAT:
|
||||
return make_float(v->d);
|
||||
case VAL_BOOL:
|
||||
return make_bool(v->i);
|
||||
case VAL_STRING:
|
||||
return make_string(v->s ? v->s : "");
|
||||
case VAL_FUNCTION:
|
||||
return make_function(v->fn); /* shallow pointer for function bytecode */
|
||||
case VAL_ARRAY: {
|
||||
const Array *a = (const Array *)v->arr;
|
||||
if (!a || a->count <= 0) {
|
||||
return make_array_from_values(NULL, 0);
|
||||
}
|
||||
/* copy items deeply */
|
||||
Value *tmp = (Value *)malloc(sizeof(Value) * a->count);
|
||||
if (!tmp) return make_nil();
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
tmp[i] = deep_copy_value(&a->items[i]);
|
||||
}
|
||||
Value out = make_array_from_values(tmp, a->count);
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
free_value(tmp[i]);
|
||||
}
|
||||
free(tmp);
|
||||
return out;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
const Map *m = (const Map *)v->map;
|
||||
if (!m || m->count <= 0) return make_map_empty();
|
||||
Value out = make_map_empty();
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
Value dv = deep_copy_value(&m->vals[i]);
|
||||
map_set(&out, m->keys[i], dv);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
return make_nil();
|
||||
}
|
||||
}
|
||||
|
||||
void free_value(Value v) {
|
||||
if (v.type == VAL_STRING && v.s) {
|
||||
free(v.s);
|
||||
} else if (v.type == VAL_ARRAY && v.arr) {
|
||||
Array *a = (Array*)v.arr;
|
||||
if (--a->refcount == 0) {
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
free_value(a->items[i]);
|
||||
}
|
||||
free(a->items);
|
||||
free(a);
|
||||
}
|
||||
} else if (v.type == VAL_MAP && v.map) {
|
||||
Map *m = (Map*)v.map;
|
||||
if (--m->refcount == 0) {
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (m->keys[i]) free(m->keys[i]);
|
||||
free_value(m->vals[i]);
|
||||
}
|
||||
free(m->keys);
|
||||
free(m->vals);
|
||||
free(m);
|
||||
}
|
||||
if (v.type == VAL_STRING && v.s) {
|
||||
free(v.s);
|
||||
} else if (v.type == VAL_ARRAY && v.arr) {
|
||||
Array *a = (Array *)v.arr;
|
||||
if (--a->refcount == 0) {
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
free_value(a->items[i]);
|
||||
}
|
||||
free(a->items);
|
||||
free(a);
|
||||
}
|
||||
/* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */
|
||||
} else if (v.type == VAL_MAP && v.map) {
|
||||
Map *m = (Map *)v.map;
|
||||
if (--m->refcount == 0) {
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (m->keys[i]) free(m->keys[i]);
|
||||
free_value(m->vals[i]);
|
||||
}
|
||||
free(m->keys);
|
||||
free(m->vals);
|
||||
free(m);
|
||||
}
|
||||
}
|
||||
/* VAL_FUNCTION: we *do not* free the Bytecode here (caller frees it) */
|
||||
}
|
||||
|
||||
void print_value(const Value *v) {
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
printf("%" PRId64, v->i);
|
||||
break;
|
||||
case VAL_FLOAT:
|
||||
printf("%.17g", v->d);
|
||||
break;
|
||||
case VAL_STRING:
|
||||
printf("%s", v->s ? v->s : "");
|
||||
break;
|
||||
case VAL_BOOL:
|
||||
printf("%s", v->i ? "true" : "false");
|
||||
break;
|
||||
case VAL_FUNCTION:
|
||||
printf("<function@%p>", (void*)v->fn);
|
||||
break;
|
||||
case VAL_ARRAY: {
|
||||
const Array *a = (const Array*)v->arr;
|
||||
printf("[");
|
||||
if (a) {
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
if (i > 0) printf(", ");
|
||||
print_value(&a->items[i]);
|
||||
}
|
||||
}
|
||||
printf("]");
|
||||
break;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
const Map *m = (const Map*)v->map;
|
||||
printf("{");
|
||||
if (m) {
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("\"%s\": ", m->keys[i] ? m->keys[i] : "");
|
||||
print_value(&m->vals[i]);
|
||||
}
|
||||
}
|
||||
printf("}");
|
||||
break;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
printf("nil");
|
||||
break;
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
printf("%" PRId64, v->i);
|
||||
break;
|
||||
case VAL_FLOAT:
|
||||
printf("%.17g", v->d);
|
||||
break;
|
||||
case VAL_STRING:
|
||||
printf("%s", v->s ? v->s : "");
|
||||
break;
|
||||
case VAL_BOOL:
|
||||
printf("%s", v->i ? "true" : "false");
|
||||
break;
|
||||
case VAL_FUNCTION:
|
||||
printf("<function@%p>", (void *)v->fn);
|
||||
break;
|
||||
case VAL_ARRAY: {
|
||||
const Array *a = (const Array *)v->arr;
|
||||
printf("[");
|
||||
if (a) {
|
||||
for (int i = 0; i < a->count; ++i) {
|
||||
if (i > 0) printf(", ");
|
||||
print_value(&a->items[i]);
|
||||
}
|
||||
}
|
||||
printf("]");
|
||||
break;
|
||||
}
|
||||
case VAL_MAP: {
|
||||
const Map *m = (const Map *)v->map;
|
||||
printf("{");
|
||||
if (m) {
|
||||
for (int i = 0; i < m->count; ++i) {
|
||||
if (i > 0) printf(", ");
|
||||
printf("\"%s\": ", m->keys[i] ? m->keys[i] : "");
|
||||
print_value(&m->vals[i]);
|
||||
}
|
||||
}
|
||||
printf("}");
|
||||
break;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
printf("nil");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int value_is_truthy(const Value *v) {
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return v->i != 0;
|
||||
case VAL_FLOAT:
|
||||
return v->d != 0.0;
|
||||
case VAL_BOOL:
|
||||
return v->i != 0;
|
||||
case VAL_STRING:
|
||||
return v->s && v->s[0] != '\0';
|
||||
case VAL_FUNCTION:
|
||||
return 1;
|
||||
case VAL_ARRAY: {
|
||||
const Array *a = (const Array*)v->arr;
|
||||
return a && a->count > 0;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return v->i != 0;
|
||||
case VAL_FLOAT:
|
||||
return v->d != 0.0;
|
||||
case VAL_BOOL:
|
||||
return v->i != 0;
|
||||
case VAL_STRING:
|
||||
return v->s && v->s[0] != '\0';
|
||||
case VAL_FUNCTION:
|
||||
return 1;
|
||||
case VAL_ARRAY: {
|
||||
const Array *a = (const Array *)v->arr;
|
||||
return a && a->count > 0;
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate a printable C string for the value; caller must free */
|
||||
char *value_to_string_alloc(const Value *v) {
|
||||
if (!v) return strdup("nil");
|
||||
char buf[128];
|
||||
switch (v->type) {
|
||||
case VAL_INT: {
|
||||
char tmp[64];
|
||||
snprintf(tmp, sizeof(tmp), "%" PRId64, v->i);
|
||||
return strdup(tmp);
|
||||
}
|
||||
case VAL_FLOAT: {
|
||||
char tmp[64];
|
||||
snprintf(tmp, sizeof(tmp), "%.17g", v->d);
|
||||
return strdup(tmp);
|
||||
}
|
||||
case VAL_STRING:
|
||||
return strdup(v->s ? v->s : "");
|
||||
case VAL_BOOL:
|
||||
return strdup(v->i ? "true" : "false");
|
||||
case VAL_FUNCTION: {
|
||||
snprintf(buf, sizeof(buf), "<function@%p>", (void*)v->fn);
|
||||
return strdup(buf);
|
||||
}
|
||||
case VAL_ARRAY: {
|
||||
int n = array_length(v);
|
||||
if (n < 0) n = 0;
|
||||
snprintf(buf, sizeof(buf), "[array n=%d]", n);
|
||||
return strdup(buf);
|
||||
}
|
||||
case VAL_MAP: {
|
||||
int n = 0;
|
||||
if (v->type == VAL_MAP && v->map) {
|
||||
const Map *m = (const Map*)v->map;
|
||||
n = m ? m->count : 0;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "{map n=%d}", n);
|
||||
return strdup(buf);
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
return strdup("nil");
|
||||
if (!v) return strdup("nil");
|
||||
char buf[128];
|
||||
switch (v->type) {
|
||||
case VAL_INT: {
|
||||
char tmp[64];
|
||||
snprintf(tmp, sizeof(tmp), "%" PRId64, v->i);
|
||||
return strdup(tmp);
|
||||
}
|
||||
case VAL_FLOAT: {
|
||||
char tmp[64];
|
||||
snprintf(tmp, sizeof(tmp), "%.17g", v->d);
|
||||
return strdup(tmp);
|
||||
}
|
||||
case VAL_STRING:
|
||||
return strdup(v->s ? v->s : "");
|
||||
case VAL_BOOL:
|
||||
return strdup(v->i ? "true" : "false");
|
||||
case VAL_FUNCTION: {
|
||||
snprintf(buf, sizeof(buf), "<function@%p>", (void *)v->fn);
|
||||
return strdup(buf);
|
||||
}
|
||||
case VAL_ARRAY: {
|
||||
int n = array_length(v);
|
||||
if (n < 0) n = 0;
|
||||
snprintf(buf, sizeof(buf), "[array n=%d]", n);
|
||||
return strdup(buf);
|
||||
}
|
||||
case VAL_MAP: {
|
||||
int n = 0;
|
||||
if (v->type == VAL_MAP && v->map) {
|
||||
const Map *m = (const Map *)v->map;
|
||||
n = m ? m->count : 0;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "{map n=%d}", n);
|
||||
return strdup(buf);
|
||||
}
|
||||
case VAL_NIL:
|
||||
default:
|
||||
return strdup("nil");
|
||||
}
|
||||
}
|
||||
|
||||
int value_equals(const Value *a, const Value *b) {
|
||||
// Numeric cross-type equality: int vs float compares numerically
|
||||
if ((a->type == VAL_INT || a->type == VAL_FLOAT) && (b->type == VAL_INT || b->type == VAL_FLOAT)) {
|
||||
double da = (a->type == VAL_INT) ? (double)a->i : a->d;
|
||||
double db = (b->type == VAL_INT) ? (double)b->i : b->d;
|
||||
return da == db;
|
||||
}
|
||||
if (a->type != b->type) return 0;
|
||||
switch (a->type) {
|
||||
case VAL_INT: return a->i == b->i;
|
||||
case VAL_BOOL: return (a->i != 0) == (b->i != 0);
|
||||
case VAL_STRING: {
|
||||
const char *sa = a->s ? a->s : "";
|
||||
const char *sb = b->s ? b->s : "";
|
||||
return strcmp(sa, sb) == 0;
|
||||
}
|
||||
default: return 0;
|
||||
}
|
||||
// Numeric cross-type equality: int vs float compares numerically
|
||||
if ((a->type == VAL_INT || a->type == VAL_FLOAT) && (b->type == VAL_INT || b->type == VAL_FLOAT)) {
|
||||
double da = (a->type == VAL_INT) ? (double)a->i : a->d;
|
||||
double db = (b->type == VAL_INT) ? (double)b->i : b->d;
|
||||
return da == db;
|
||||
}
|
||||
if (a->type != b->type) return 0;
|
||||
switch (a->type) {
|
||||
case VAL_INT:
|
||||
return a->i == b->i;
|
||||
case VAL_BOOL:
|
||||
return (a->i != 0) == (b->i != 0);
|
||||
case VAL_STRING: {
|
||||
const char *sa = a->s ? a->s : "";
|
||||
const char *sb = b->s ? b->s : "";
|
||||
return strcmp(sa, sb) == 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
84
src/value.h
84
src/value.h
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* @file value.h
|
||||
* @file value.h
|
||||
* @brief Defines the Value type and associated functions for the Fun VM.
|
||||
*
|
||||
* This file defines the `Value` type, which represents all possible data types
|
||||
|
|
@ -45,26 +45,26 @@ struct Array; /* forward */
|
|||
struct Map; /* forward */
|
||||
|
||||
typedef enum {
|
||||
VAL_INT,
|
||||
VAL_BOOL,
|
||||
VAL_STRING,
|
||||
VAL_FUNCTION,
|
||||
VAL_ARRAY,
|
||||
VAL_MAP,
|
||||
VAL_NIL,
|
||||
VAL_FLOAT
|
||||
VAL_INT,
|
||||
VAL_BOOL,
|
||||
VAL_STRING,
|
||||
VAL_FUNCTION,
|
||||
VAL_ARRAY,
|
||||
VAL_MAP,
|
||||
VAL_NIL,
|
||||
VAL_FLOAT
|
||||
} ValueType;
|
||||
|
||||
typedef struct {
|
||||
ValueType type;
|
||||
union {
|
||||
int64_t i;
|
||||
double d;
|
||||
char *s;
|
||||
struct Bytecode *fn;
|
||||
struct Array *arr;
|
||||
struct Map *map;
|
||||
};
|
||||
ValueType type;
|
||||
union {
|
||||
int64_t i;
|
||||
double d;
|
||||
char *s;
|
||||
struct Bytecode *fn;
|
||||
struct Array *arr;
|
||||
struct Map *map;
|
||||
};
|
||||
} Value;
|
||||
|
||||
/* constructors / helpers */
|
||||
|
|
@ -77,46 +77,46 @@ Value make_float(double v);
|
|||
|
||||
/* arrays */
|
||||
Value make_array_from_values(const Value *vals, int count); /* deep-copies vals */
|
||||
int array_length(const Value *v); /* returns -1 if not array */
|
||||
int array_get_copy(const Value *v, int index, Value *out); /* returns 0 on error; out = copy_value(item) */
|
||||
int array_set(Value *v, int index, Value newElem); /* returns 0 on error; takes ownership of newElem */
|
||||
int array_push(Value *v, Value newElem); /* returns new length or -1 on error */
|
||||
int array_pop(Value *v, Value *out); /* returns 1 on success, out takes ownership */
|
||||
int array_insert(Value *v, int index, Value newElem); /* returns new length or -1 */
|
||||
int array_remove(Value *v, int index, Value *out); /* returns 1 on success */
|
||||
Value array_slice(const Value *v, int start, int end); /* negative end means till end */
|
||||
Value array_concat(const Value *a, const Value *b); /* returns new array */
|
||||
int array_length(const Value *v); /* returns -1 if not array */
|
||||
int array_get_copy(const Value *v, int index, Value *out); /* returns 0 on error; out = copy_value(item) */
|
||||
int array_set(Value *v, int index, Value newElem); /* returns 0 on error; takes ownership of newElem */
|
||||
int array_push(Value *v, Value newElem); /* returns new length or -1 on error */
|
||||
int array_pop(Value *v, Value *out); /* returns 1 on success, out takes ownership */
|
||||
int array_insert(Value *v, int index, Value newElem); /* returns new length or -1 */
|
||||
int array_remove(Value *v, int index, Value *out); /* returns 1 on success */
|
||||
Value array_slice(const Value *v, int start, int end); /* negative end means till end */
|
||||
Value array_concat(const Value *a, const Value *b); /* returns new array */
|
||||
|
||||
/* maps (string keys) */
|
||||
Value make_map_empty(void); /* new empty map */
|
||||
int map_set(Value *m, const char *key, Value v); /* 1 on ok (takes ownership of v) */
|
||||
int map_get_copy(const Value *m, const char *key, Value *out);/* 1 on found, out=copy */
|
||||
int map_has(const Value *m, const char *key); /* 1/0 */
|
||||
Value map_keys_array(const Value *m); /* array of strings */
|
||||
Value map_values_array(const Value *m); /* array of values (copies) */
|
||||
Value make_map_empty(void); /* new empty map */
|
||||
int map_set(Value *m, const char *key, Value v); /* 1 on ok (takes ownership of v) */
|
||||
int map_get_copy(const Value *m, const char *key, Value *out); /* 1 on found, out=copy */
|
||||
int map_has(const Value *m, const char *key); /* 1/0 */
|
||||
Value map_keys_array(const Value *m); /* array of strings */
|
||||
Value map_values_array(const Value *m); /* array of values (copies) */
|
||||
|
||||
/* copy/free */
|
||||
Value copy_value(const Value *v); /* deep for strings, RC for arrays/maps, shallow fn */
|
||||
Value copy_value(const Value *v); /* deep for strings, RC for arrays/maps, shallow fn */
|
||||
Value deep_copy_value(const Value *v); /* deep copy including arrays/maps */
|
||||
void free_value(Value v); /* frees owned resources */
|
||||
void free_value(Value v); /* frees owned resources */
|
||||
|
||||
/* utilities */
|
||||
void print_value(const Value *v);
|
||||
int value_is_truthy(const Value *v);
|
||||
int value_equals(const Value *a, const Value *b); /* int/string equality */
|
||||
int value_equals(const Value *a, const Value *b); /* int/string equality */
|
||||
|
||||
/* stringify into a newly-allocated C string; caller must free */
|
||||
char *value_to_string_alloc(const Value *v);
|
||||
|
||||
/* array utils */
|
||||
int array_contains(const Value *arr, const Value *needle); /* 1/0 */
|
||||
int array_index_of(const Value *arr, const Value *needle); /* idx or -1 */
|
||||
void array_clear(Value *arr); /* free elements, count=0 */
|
||||
int array_contains(const Value *arr, const Value *needle); /* 1/0 */
|
||||
int array_index_of(const Value *arr, const Value *needle); /* idx or -1 */
|
||||
void array_clear(Value *arr); /* free elements, count=0 */
|
||||
|
||||
/* string helpers returning newly allocated C strings or arrays */
|
||||
char *string_substr(const char *s, int start, int len); /* clamps bounds */
|
||||
int string_find(const char *hay, const char *needle); /* index or -1 */
|
||||
Value string_split_to_array(const char *s, const char *sep); /* array of strings */
|
||||
char *array_join_with_sep(const Value *arr, const char *sep); /* join items as strings */
|
||||
int string_find(const char *hay, const char *needle); /* index or -1 */
|
||||
Value string_split_to_array(const char *s, const char *sep); /* array of strings */
|
||||
char *array_join_with_sep(const Value *arr, const char *sep); /* join items as strings */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
153
src/vm.h
153
src/vm.h
|
|
@ -20,94 +20,93 @@
|
|||
#define STACK_SIZE 1024
|
||||
|
||||
static const char *opcode_names[] = {
|
||||
"NOP","LOAD_CONST","LOAD_LOCAL","STORE_LOCAL",
|
||||
"LOAD_GLOBAL","STORE_GLOBAL","ADD","SUB","MUL","DIV",
|
||||
"LT","LTE","GT","GTE","EQ","NEQ","POP","JUMP",
|
||||
"JUMP_IF_FALSE","CALL","RETURN","PRINT","ECHO","HALT",
|
||||
"LINE",
|
||||
"MOD","AND","OR","NOT","DUP","SWAP",
|
||||
"MAKE_ARRAY","INDEX_GET","INDEX_SET",
|
||||
"LEN","PUSH","APOP","SET","INSERT","REMOVE","SLICE",
|
||||
"TO_NUMBER","TO_STRING","CAST","TYPEOF",
|
||||
"SPLIT","JOIN","SUBSTR","FIND",
|
||||
"REGEX_MATCH","REGEX_SEARCH","REGEX_REPLACE",
|
||||
"CONTAINS","INDEX_OF","CLEAR",
|
||||
"ENUMERATE","ZIP",
|
||||
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT",
|
||||
"MAKE_MAP","KEYS","VALUES","HAS_KEY",
|
||||
"READ_FILE","WRITE_FILE","ENV","INPUT_LINE","PROC_RUN","PROC_SYSTEM",
|
||||
"TIME_NOW_MS","CLOCK_MONO_MS","DATE_FORMAT",
|
||||
"THREAD_SPAWN","THREAD_JOIN","SLEEP_MS",
|
||||
"RANDOM_NUMBER",
|
||||
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR",
|
||||
"JSON_PARSE","JSON_STRINGIFY","JSON_FROM_FILE","JSON_TO_FILE",
|
||||
"CURL_GET","CURL_POST","CURL_DOWNLOAD",
|
||||
"SQLITE_OPEN","SQLITE_CLOSE","SQLITE_EXEC","SQLITE_QUERY",
|
||||
"LIBSQL_OPEN","LIBSQL_CLOSE","LIBSQL_EXEC","LIBSQL_QUERY",
|
||||
"PCSC_ESTABLISH","PCSC_RELEASE","PCSC_LIST_READERS","PCSC_CONNECT","PCSC_DISCONNECT","PCSC_TRANSMIT",
|
||||
"PCRE2_TEST","PCRE2_MATCH","PCRE2_FINDALL",
|
||||
"INI_LOAD","INI_FREE","INI_GET_STRING","INI_GET_INT","INI_GET_DOUBLE","INI_GET_BOOL","INI_SET","INI_UNSET","INI_SAVE",
|
||||
"XML_PARSE","XML_ROOT","XML_NAME","XML_TEXT",
|
||||
"SOCK_TCP_LISTEN","SOCK_TCP_ACCEPT","SOCK_TCP_CONNECT","SOCK_SEND","SOCK_RECV","SOCK_CLOSE","SOCK_UNIX_LISTEN","SOCK_UNIX_CONNECT",
|
||||
"EXIT",
|
||||
"OS_LIST_DIR",
|
||||
"TK_BIND",
|
||||
"SERIAL_OPEN","SERIAL_CONFIG","SERIAL_SEND","SERIAL_RECV","SERIAL_CLOSE",
|
||||
"TK_EVAL","TK_RESULT","TK_LOOP","TK_WM_TITLE","TK_LABEL","TK_BUTTON","TK_PACK",
|
||||
"TRY_PUSH","TRY_POP","THROW",
|
||||
"FMIN","FMAX",
|
||||
/* Rust FFI demo */
|
||||
"RUST_HELLO","RUST_HELLO_ARGS","RUST_HELLO_ARGS_RETURN","RUST_GET_SP","RUST_SET_EXIT",
|
||||
/* C++ demo */
|
||||
"CPP_ADD",
|
||||
/* Notcurses TUI (optional) */
|
||||
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
|
||||
};
|
||||
"NOP", "LOAD_CONST", "LOAD_LOCAL", "STORE_LOCAL",
|
||||
"LOAD_GLOBAL", "STORE_GLOBAL", "ADD", "SUB", "MUL", "DIV",
|
||||
"LT", "LTE", "GT", "GTE", "EQ", "NEQ", "POP", "JUMP",
|
||||
"JUMP_IF_FALSE", "CALL", "RETURN", "PRINT", "ECHO", "HALT",
|
||||
"LINE",
|
||||
"MOD", "AND", "OR", "NOT", "DUP", "SWAP",
|
||||
"MAKE_ARRAY", "INDEX_GET", "INDEX_SET",
|
||||
"LEN", "PUSH", "APOP", "SET", "INSERT", "REMOVE", "SLICE",
|
||||
"TO_NUMBER", "TO_STRING", "CAST", "TYPEOF",
|
||||
"SPLIT", "JOIN", "SUBSTR", "FIND",
|
||||
"REGEX_MATCH", "REGEX_SEARCH", "REGEX_REPLACE",
|
||||
"CONTAINS", "INDEX_OF", "CLEAR",
|
||||
"ENUMERATE", "ZIP",
|
||||
"MIN", "MAX", "CLAMP", "ABS", "POW", "RANDOM_SEED", "RANDOM_INT",
|
||||
"MAKE_MAP", "KEYS", "VALUES", "HAS_KEY",
|
||||
"READ_FILE", "WRITE_FILE", "ENV", "INPUT_LINE", "PROC_RUN", "PROC_SYSTEM",
|
||||
"TIME_NOW_MS", "CLOCK_MONO_MS", "DATE_FORMAT",
|
||||
"THREAD_SPAWN", "THREAD_JOIN", "SLEEP_MS",
|
||||
"RANDOM_NUMBER",
|
||||
"BAND", "BOR", "BXOR", "BNOT", "SHL", "SHR", "ROTL", "ROTR",
|
||||
"JSON_PARSE", "JSON_STRINGIFY", "JSON_FROM_FILE", "JSON_TO_FILE",
|
||||
"CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
|
||||
"SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
|
||||
"LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
|
||||
"PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
|
||||
"PCRE2_TEST", "PCRE2_MATCH", "PCRE2_FINDALL",
|
||||
"INI_LOAD", "INI_FREE", "INI_GET_STRING", "INI_GET_INT", "INI_GET_DOUBLE", "INI_GET_BOOL", "INI_SET", "INI_UNSET", "INI_SAVE",
|
||||
"XML_PARSE", "XML_ROOT", "XML_NAME", "XML_TEXT",
|
||||
"SOCK_TCP_LISTEN", "SOCK_TCP_ACCEPT", "SOCK_TCP_CONNECT", "SOCK_SEND", "SOCK_RECV", "SOCK_CLOSE", "SOCK_UNIX_LISTEN", "SOCK_UNIX_CONNECT",
|
||||
"EXIT",
|
||||
"OS_LIST_DIR",
|
||||
"TK_BIND",
|
||||
"SERIAL_OPEN", "SERIAL_CONFIG", "SERIAL_SEND", "SERIAL_RECV", "SERIAL_CLOSE",
|
||||
"TK_EVAL", "TK_RESULT", "TK_LOOP", "TK_WM_TITLE", "TK_LABEL", "TK_BUTTON", "TK_PACK",
|
||||
"TRY_PUSH", "TRY_POP", "THROW",
|
||||
"FMIN", "FMAX",
|
||||
/* Rust FFI demo */
|
||||
"RUST_HELLO", "RUST_HELLO_ARGS", "RUST_HELLO_ARGS_RETURN", "RUST_GET_SP", "RUST_SET_EXIT",
|
||||
/* C++ demo */
|
||||
"CPP_ADD",
|
||||
/* Notcurses TUI (optional) */
|
||||
"NC_INIT", "NC_SHUTDOWN", "NC_CLEAR", "NC_DRAW_TEXT", "NC_GETCH"};
|
||||
|
||||
typedef struct {
|
||||
Bytecode *fn;
|
||||
int ip;
|
||||
Value locals[MAX_FRAME_LOCALS];
|
||||
/* exception handling (per-frame) */
|
||||
int try_stack[16];
|
||||
int try_sp; /* -1 when empty */
|
||||
Bytecode *fn;
|
||||
int ip;
|
||||
Value locals[MAX_FRAME_LOCALS];
|
||||
/* exception handling (per-frame) */
|
||||
int try_stack[16];
|
||||
int try_sp; /* -1 when empty */
|
||||
} Frame;
|
||||
|
||||
struct VM {
|
||||
Value stack[STACK_SIZE];
|
||||
int sp;
|
||||
Value stack[STACK_SIZE];
|
||||
int sp;
|
||||
|
||||
Frame frames[MAX_FRAMES];
|
||||
int fp; // frame pointer, -1 when no frame
|
||||
Frame frames[MAX_FRAMES];
|
||||
int fp; // frame pointer, -1 when no frame
|
||||
|
||||
Value globals[MAX_GLOBALS];
|
||||
Value globals[MAX_GLOBALS];
|
||||
|
||||
Value output[OUTPUT_SIZE]; // store printed values
|
||||
int output_count;
|
||||
int output_is_partial[OUTPUT_SIZE]; // 1 when the corresponding output entry should not end with newline (echo)
|
||||
Value output[OUTPUT_SIZE]; // store printed values
|
||||
int output_count;
|
||||
int output_is_partial[OUTPUT_SIZE]; // 1 when the corresponding output entry should not end with newline (echo)
|
||||
|
||||
long long instr_count; // executed instructions in the last vm_run
|
||||
long long instr_count; // executed instructions in the last vm_run
|
||||
|
||||
int current_line; // last executed source line (debug)
|
||||
int current_line; // last executed source line (debug)
|
||||
|
||||
int exit_code; // process exit code set by OP_EXIT
|
||||
int exit_code; // process exit code set by OP_EXIT
|
||||
|
||||
int trace_enabled; // when non-zero, print executed ops and stack
|
||||
int repl_on_error; // when non-zero, enter REPL on runtime error (preserve stack)
|
||||
int (*on_error_repl)(struct VM *vm); // optional hook to run REPL on error
|
||||
int trace_enabled; // when non-zero, print executed ops and stack
|
||||
int repl_on_error; // when non-zero, enter REPL on runtime error (preserve stack)
|
||||
int (*on_error_repl)(struct VM *vm); // optional hook to run REPL on error
|
||||
|
||||
/* --- Debugger state --- */
|
||||
int debug_step_mode; // 0 none, 1 step, 2 next, 3 finish
|
||||
int debug_step_target_fp; // target frame pointer for next/finish
|
||||
long long debug_step_start_ic; // instruction count snapshot when step/next requested
|
||||
int debug_stop_requested; // force a pause at loop top
|
||||
/* --- Debugger state --- */
|
||||
int debug_step_mode; // 0 none, 1 step, 2 next, 3 finish
|
||||
int debug_step_target_fp; // target frame pointer for next/finish
|
||||
long long debug_step_start_ic; // instruction count snapshot when step/next requested
|
||||
int debug_stop_requested; // force a pause at loop top
|
||||
|
||||
struct {
|
||||
char *file; // strdup'ed file path
|
||||
int line; // 1-based line
|
||||
int active; // 1 if active
|
||||
} breakpoints[64];
|
||||
int break_count; // number of active breakpoints
|
||||
struct {
|
||||
char *file; // strdup'ed file path
|
||||
int line; // 1-based line
|
||||
int active; // 1 if active
|
||||
} breakpoints[64];
|
||||
int break_count; // number of active breakpoints
|
||||
};
|
||||
|
||||
typedef struct VM VM;
|
||||
|
|
@ -136,8 +135,8 @@ void vm_raise_error(VM *vm, const char *msg);
|
|||
|
||||
/* --- Debugger API --- */
|
||||
void vm_debug_reset(VM *vm);
|
||||
int vm_debug_add_breakpoint(VM *vm, const char *file, int line); // returns id >=0 or -1
|
||||
int vm_debug_delete_breakpoint(VM *vm, int id); // returns 1 on success
|
||||
int vm_debug_add_breakpoint(VM *vm, const char *file, int line); // returns id >=0 or -1
|
||||
int vm_debug_delete_breakpoint(VM *vm, int id); // returns 1 on success
|
||||
void vm_debug_clear_breakpoints(VM *vm);
|
||||
void vm_debug_list_breakpoints(VM *vm);
|
||||
void vm_debug_request_step(VM *vm);
|
||||
|
|
@ -146,7 +145,7 @@ void vm_debug_request_finish(VM *vm);
|
|||
void vm_debug_request_continue(VM *vm);
|
||||
|
||||
static inline int opcode_is_valid(int op) {
|
||||
return op >= OP_NOP && op <= OP_NC_GETCH; // all current opcodes (including optional NC_*)
|
||||
return op >= OP_NOP && op <= OP_NC_GETCH; // all current opcodes (including optional NC_*)
|
||||
}
|
||||
|
||||
/* --- Minimal C ABI helpers for FFI (Rust opcode experiments) --- */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
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