1
0
Fork 0
forked from fun/fun

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

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

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(fun VERSION 0.38.16 LANGUAGES C) project(fun VERSION 0.39.0 LANGUAGES C)
set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
@ -46,7 +46,16 @@ message(STATUS "===========================")
# Convenience aggregate target (like 'build' in Makefile) # Convenience aggregate target (like 'build' in Makefile)
add_custom_target(build add_custom_target(build
DEPENDS fun fun_test test_opcodes DEPENDS fun funstx fun_test test_opcodes
)
# Formatting helper target: run clang-format over C/C++ sources using .clang-format
add_custom_target(format
COMMAND ${CMAKE_COMMAND} -E echo "Running clang-format on sources..."
COMMAND /bin/sh -c "command -v clang-format >/dev/null 2>&1 || { echo 'clang-format not found in PATH' >&2; exit 1; }"
COMMAND /bin/sh -c "set -e; for pat in '*.c' '*.h' '*.cpp' '*.hpp'; do find ${CMAKE_SOURCE_DIR}/src -type f -name \"\$pat\" -print0; done | xargs -0 -n 50 clang-format -i"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Apply .clang-format (IndentWidth=2) to all source files"
) )
# --- Rust (Cargo) integration: optionally build and link a staticlib with opcode examples --- # --- Rust (Cargo) integration: optionally build and link a staticlib with opcode examples ---
@ -400,6 +409,11 @@ install(TARGETS fun
RUNTIME DESTINATION /usr/bin RUNTIME DESTINATION /usr/bin
) )
# Also install the syntax-checker CLI by default
install(TARGETS funstx
RUNTIME DESTINATION /usr/bin
)
# Libs # Libs
install(DIRECTORY lib/ install(DIRECTORY lib/
DESTINATION /usr/share/fun/lib DESTINATION /usr/share/fun/lib

View file

@ -139,6 +139,12 @@ if(FUN_WITH_REPL)
endif() endif()
target_link_libraries(fun PRIVATE fun_core) target_link_libraries(fun PRIVATE fun_core)
# Executable: funstx (syntax checker CLI)
add_executable(funstx
${CMAKE_SOURCE_DIR}/src/funstx.c
)
target_link_libraries(funstx PRIVATE fun_core)
# Internal test programs # Internal test programs
add_executable(fun_test add_executable(fun_test
${CMAKE_SOURCE_DIR}/src/fun_test.c) ${CMAKE_SOURCE_DIR}/src/fun_test.c)

View file

@ -20,7 +20,7 @@
#include <encoding/base64.fun> #include <encoding/base64.fun>
print("=== Base64 demo ===") print("=== Base64 demo ===")
// Bytes for the ASCII string "Hello" // Bytes for the ASCII string "Hello"
bytes = [0x48, 0x65, 0x6c, 0x6c, 0x6f] bytes = [0x48, 0x65, 0x6c, 0x6c, 0x6f]

View file

@ -8,11 +8,11 @@
*/ */
#include "bytecode.h" #include "bytecode.h"
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
Bytecode *bytecode_new(void) { Bytecode *bytecode_new(void) {
Bytecode *bc = (Bytecode*)malloc(sizeof(Bytecode)); Bytecode *bc = (Bytecode *)malloc(sizeof(Bytecode));
bc->instructions = NULL; bc->instructions = NULL;
bc->instr_count = 0; bc->instr_count = 0;
bc->constants = NULL; bc->constants = NULL;
@ -23,13 +23,13 @@ Bytecode *bytecode_new(void) {
} }
int bytecode_add_constant(Bytecode *bc, Value v) { int bytecode_add_constant(Bytecode *bc, Value v) {
bc->constants = (Value*)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1)); bc->constants = (Value *)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1));
bc->constants[bc->const_count] = copy_value(&v); bc->constants[bc->const_count] = copy_value(&v);
return bc->const_count++; return bc->const_count++;
} }
int bytecode_add_instruction(Bytecode *bc, OpCode op, int32_t operand) { 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 = (Instruction *)realloc(bc->instructions, sizeof(Instruction) * (bc->instr_count + 1));
bc->instructions[bc->instr_count].op = op; bc->instructions[bc->instr_count].op = op;
bc->instructions[bc->instr_count].operand = operand; bc->instructions[bc->instr_count].operand = operand;
return bc->instr_count++; return bc->instr_count++;
@ -48,178 +48,343 @@ void bytecode_free(Bytecode *bc) {
} }
free(bc->constants); free(bc->constants);
free(bc->instructions); free(bc->instructions);
if (bc->name) free((void*)bc->name); if (bc->name) free((void *)bc->name);
if (bc->source_file) free((void*)bc->source_file); if (bc->source_file) free((void *)bc->source_file);
free(bc); free(bc);
} }
static const char *opcode_name(OpCode op) { static const char *opcode_name(OpCode op) {
switch (op) { switch (op) {
case OP_NOP: return "NOP"; case OP_NOP:
case OP_LOAD_CONST: return "LOAD_CONST"; return "NOP";
case OP_LOAD_LOCAL: return "LOAD_LOCAL"; case OP_LOAD_CONST:
case OP_STORE_LOCAL: return "STORE_LOCAL"; return "LOAD_CONST";
case OP_LOAD_GLOBAL: return "LOAD_GLOBAL"; case OP_LOAD_LOCAL:
case OP_STORE_GLOBAL: return "STORE_GLOBAL"; return "LOAD_LOCAL";
case OP_ADD: return "ADD"; case OP_STORE_LOCAL:
case OP_SUB: return "SUB"; return "STORE_LOCAL";
case OP_MUL: return "MUL"; case OP_LOAD_GLOBAL:
case OP_DIV: return "DIV"; return "LOAD_GLOBAL";
case OP_LT: return "LT"; case OP_STORE_GLOBAL:
case OP_LTE: return "LTE"; return "STORE_GLOBAL";
case OP_GT: return "GT"; case OP_ADD:
case OP_GTE: return "GTE"; return "ADD";
case OP_EQ: return "EQ"; case OP_SUB:
case OP_NEQ: return "NEQ"; return "SUB";
case OP_POP: return "POP"; case OP_MUL:
case OP_JUMP: return "JUMP"; return "MUL";
case OP_JUMP_IF_FALSE: return "JUMP_IF_FALSE"; case OP_DIV:
case OP_CALL: return "CALL"; return "DIV";
case OP_RETURN: return "RETURN"; case OP_LT:
case OP_PRINT: return "PRINT"; return "LT";
case OP_ECHO: return "ECHO"; case OP_LTE:
case OP_HALT: return "HALT"; return "LTE";
case OP_MOD: return "MOD"; case OP_GT:
case OP_AND: return "AND"; return "GT";
case OP_OR: return "OR"; case OP_GTE:
case OP_NOT: return "NOT"; return "GTE";
case OP_DUP: return "DUP"; case OP_EQ:
case OP_SWAP: return "SWAP"; return "EQ";
case OP_MAKE_ARRAY: return "MAKE_ARRAY"; case OP_NEQ:
case OP_INDEX_GET: return "INDEX_GET"; return "NEQ";
case OP_INDEX_SET: return "INDEX_SET"; case OP_POP:
case OP_LEN: return "LEN"; return "POP";
case OP_PUSH: return "ARR_PUSH"; case OP_JUMP:
case OP_APOP: return "ARR_POP"; return "JUMP";
case OP_SET: return "ARR_SET"; case OP_JUMP_IF_FALSE:
case OP_INSERT: return "ARR_INSERT"; return "JUMP_IF_FALSE";
case OP_REMOVE: return "ARR_REMOVE"; case OP_CALL:
case OP_SLICE: return "SLICE"; return "CALL";
case OP_TO_NUMBER: return "TO_NUMBER"; case OP_RETURN:
case OP_TO_STRING: return "TO_STRING"; return "RETURN";
case OP_TYPEOF: return "TYPEOF"; case OP_PRINT:
case OP_CAST: return "CAST"; return "PRINT";
case OP_SPLIT: return "SPLIT"; case OP_ECHO:
case OP_JOIN: return "JOIN"; return "ECHO";
case OP_SUBSTR: return "SUBSTR"; case OP_HALT:
case OP_FIND: return "FIND"; return "HALT";
case OP_REGEX_MATCH: return "REGEX_MATCH"; case OP_MOD:
case OP_REGEX_SEARCH: return "REGEX_SEARCH"; return "MOD";
case OP_REGEX_REPLACE: return "REGEX_REPLACE"; case OP_AND:
case OP_CONTAINS: return "CONTAINS"; return "AND";
case OP_INDEX_OF: return "INDEX_OF"; case OP_OR:
case OP_CLEAR: return "CLEAR"; return "OR";
case OP_ENUMERATE: return "ENUMERATE"; case OP_NOT:
case OP_ZIP: return "ZIP"; return "NOT";
case OP_MIN: return "MIN"; case OP_DUP:
case OP_MAX: return "MAX"; return "DUP";
case OP_CLAMP: return "CLAMP"; case OP_SWAP:
case OP_ABS: return "ABS"; return "SWAP";
case OP_POW: return "POW"; case OP_MAKE_ARRAY:
case OP_RANDOM_SEED: return "RANDOM_SEED"; return "MAKE_ARRAY";
case OP_RANDOM_INT: return "RANDOM_INT"; case OP_INDEX_GET:
case OP_MAKE_MAP: return "MAKE_MAP"; return "INDEX_GET";
case OP_KEYS: return "KEYS"; case OP_INDEX_SET:
case OP_VALUES: return "VALUES"; return "INDEX_SET";
case OP_HAS_KEY: return "HAS_KEY"; case OP_LEN:
case OP_READ_FILE: return "READ_FILE"; return "LEN";
case OP_WRITE_FILE: return "WRITE_FILE"; case OP_PUSH:
case OP_ENV: return "ENV"; return "ARR_PUSH";
case OP_INPUT_LINE: return "INPUT_LINE"; case OP_APOP:
case OP_PROC_RUN: return "PROC_RUN"; return "ARR_POP";
case OP_PROC_SYSTEM: return "PROC_SYSTEM"; case OP_SET:
case OP_TIME_NOW_MS: return "TIME_NOW_MS"; return "ARR_SET";
case OP_CLOCK_MONO_MS: return "CLOCK_MONO_MS"; case OP_INSERT:
case OP_DATE_FORMAT: return "DATE_FORMAT"; return "ARR_INSERT";
case OP_ENV_ALL: return "ENV_ALL"; case OP_REMOVE:
case OP_FUN_VERSION: return "FUN_VERSION"; return "ARR_REMOVE";
case OP_THREAD_SPAWN: return "THREAD_SPAWN"; case OP_SLICE:
case OP_THREAD_JOIN: return "THREAD_JOIN"; return "SLICE";
case OP_SLEEP_MS: return "SLEEP_MS"; case OP_TO_NUMBER:
case OP_RANDOM_NUMBER: return "RANDOM_NUMBER"; return "TO_NUMBER";
case OP_BAND: return "BAND"; case OP_TO_STRING:
case OP_BOR: return "BOR"; return "TO_STRING";
case OP_BXOR: return "BXOR"; case OP_TYPEOF:
case OP_BNOT: return "BNOT"; return "TYPEOF";
case OP_SHL: return "SHL"; case OP_CAST:
case OP_SHR: return "SHR"; return "CAST";
case OP_ROTL: return "ROTL"; case OP_SPLIT:
case OP_ROTR: return "ROTR"; return "SPLIT";
case OP_JSON_PARSE: return "JSON_PARSE"; case OP_JOIN:
case OP_JSON_STRINGIFY: return "JSON_STRINGIFY"; return "JOIN";
case OP_JSON_FROM_FILE: return "JSON_FROM_FILE"; case OP_SUBSTR:
case OP_JSON_TO_FILE: return "JSON_TO_FILE"; return "SUBSTR";
case OP_CURL_GET: return "CURL_GET"; case OP_FIND:
case OP_CURL_POST: return "CURL_POST"; return "FIND";
case OP_CURL_DOWNLOAD: return "CURL_DOWNLOAD"; case OP_REGEX_MATCH:
case OP_SQLITE_OPEN: return "SQLITE_OPEN"; return "REGEX_MATCH";
case OP_SQLITE_CLOSE: return "SQLITE_CLOSE"; case OP_REGEX_SEARCH:
case OP_SQLITE_EXEC: return "SQLITE_EXEC"; return "REGEX_SEARCH";
case OP_SQLITE_QUERY: return "SQLITE_QUERY"; case OP_REGEX_REPLACE:
case OP_LIBSQL_OPEN: return "LIBSQL_OPEN"; return "REGEX_REPLACE";
case OP_LIBSQL_CLOSE: return "LIBSQL_CLOSE"; case OP_CONTAINS:
case OP_LIBSQL_EXEC: return "LIBSQL_EXEC"; return "CONTAINS";
case OP_LIBSQL_QUERY: return "LIBSQL_QUERY"; case OP_INDEX_OF:
case OP_PCSC_ESTABLISH: return "PCSC_ESTABLISH"; return "INDEX_OF";
case OP_PCSC_RELEASE: return "PCSC_RELEASE"; case OP_CLEAR:
case OP_PCSC_LIST_READERS: return "PCSC_LIST_READERS"; return "CLEAR";
case OP_PCSC_CONNECT: return "PCSC_CONNECT"; case OP_ENUMERATE:
case OP_PCSC_DISCONNECT: return "PCSC_DISCONNECT"; return "ENUMERATE";
case OP_PCSC_TRANSMIT: return "PCSC_TRANSMIT"; case OP_ZIP:
case OP_PCRE2_TEST: return "PCRE2_TEST"; return "ZIP";
case OP_PCRE2_MATCH: return "PCRE2_MATCH"; case OP_MIN:
case OP_PCRE2_FINDALL: return "PCRE2_FINDALL"; return "MIN";
case OP_OPENSSL_MD5: return "OPENSSL_MD5"; case OP_MAX:
case OP_OPENSSL_SHA256: return "OPENSSL_SHA256"; return "MAX";
case OP_OPENSSL_SHA512: return "OPENSSL_SHA512"; case OP_CLAMP:
case OP_OPENSSL_RIPEMD160: return "OPENSSL_RIPEMD160"; return "CLAMP";
case OP_LIBRESSL_MD5: return "LIBRESSL_MD5"; case OP_ABS:
case OP_LIBRESSL_SHA256: return "LIBRESSL_SHA256"; return "ABS";
case OP_LIBRESSL_SHA512: return "LIBRESSL_SHA512"; case OP_POW:
case OP_LIBRESSL_RIPEMD160: return "LIBRESSL_RIPEMD160"; return "POW";
case OP_INI_LOAD: return "INI_LOAD"; case OP_RANDOM_SEED:
case OP_INI_FREE: return "INI_FREE"; return "RANDOM_SEED";
case OP_INI_GET_STRING: return "INI_GET_STRING"; case OP_RANDOM_INT:
case OP_INI_GET_INT: return "INI_GET_INT"; return "RANDOM_INT";
case OP_INI_GET_DOUBLE: return "INI_GET_DOUBLE"; case OP_MAKE_MAP:
case OP_INI_GET_BOOL: return "INI_GET_BOOL"; return "MAKE_MAP";
case OP_INI_SET: return "INI_SET"; case OP_KEYS:
case OP_INI_UNSET: return "INI_UNSET"; return "KEYS";
case OP_INI_SAVE: return "INI_SAVE"; case OP_VALUES:
case OP_XML_PARSE: return "XML_PARSE"; return "VALUES";
case OP_XML_ROOT: return "XML_ROOT"; case OP_HAS_KEY:
case OP_XML_NAME: return "XML_NAME"; return "HAS_KEY";
case OP_XML_TEXT: return "XML_TEXT"; case OP_READ_FILE:
case OP_TK_EVAL: return "TK_EVAL"; return "READ_FILE";
case OP_TK_RESULT: return "TK_RESULT"; case OP_WRITE_FILE:
case OP_TK_LOOP: return "TK_LOOP"; return "WRITE_FILE";
case OP_TK_WM_TITLE: return "TK_WM_TITLE"; case OP_ENV:
case OP_TK_LABEL: return "TK_LABEL"; return "ENV";
case OP_TK_BUTTON: return "TK_BUTTON"; case OP_INPUT_LINE:
case OP_TK_PACK: return "TK_PACK"; return "INPUT_LINE";
case OP_FLOOR: return "FLOOR"; case OP_PROC_RUN:
case OP_CEIL: return "CEIL"; return "PROC_RUN";
case OP_TRUNC: return "TRUNC"; case OP_PROC_SYSTEM:
case OP_ROUND: return "ROUND"; return "PROC_SYSTEM";
case OP_SIN: return "SIN"; case OP_TIME_NOW_MS:
case OP_COS: return "COS"; return "TIME_NOW_MS";
case OP_TAN: return "TAN"; case OP_CLOCK_MONO_MS:
case OP_EXP: return "EXP"; return "CLOCK_MONO_MS";
case OP_LOG: return "LOG"; case OP_DATE_FORMAT:
case OP_LOG10: return "LOG10"; return "DATE_FORMAT";
case OP_SQRT: return "SQRT"; case OP_ENV_ALL:
case OP_GCD: return "GCD"; return "ENV_ALL";
case OP_LCM: return "LCM"; case OP_FUN_VERSION:
case OP_ISQRT: return "ISQRT"; return "FUN_VERSION";
case OP_SIGN: return "SIGN"; case OP_THREAD_SPAWN:
case OP_FMIN: return "FMIN"; return "THREAD_SPAWN";
case OP_FMAX: return "FMAX"; case OP_THREAD_JOIN:
case OP_RUST_HELLO: return "RUST_HELLO"; return "THREAD_JOIN";
case OP_RUST_HELLO_ARGS: return "RUST_HELLO_ARGS"; case OP_SLEEP_MS:
case OP_RUST_HELLO_ARGS_RETURN: return "RUST_HELLO_ARGS_RETURN"; return "SLEEP_MS";
case OP_RUST_GET_SP: return "RUST_GET_SP"; case OP_RANDOM_NUMBER:
case OP_RUST_SET_EXIT: return "RUST_SET_EXIT"; return "RANDOM_NUMBER";
default: return "???"; 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 "???";
} }
} }

View file

@ -10,8 +10,8 @@
#ifndef FUN_BYTECODE_H #ifndef FUN_BYTECODE_H
#define FUN_BYTECODE_H #define FUN_BYTECODE_H
#include <stdint.h>
#include "value.h" #include "value.h"
#include <stdint.h>
// VM opcodes // VM opcodes
typedef enum { typedef enum {

15
src/external/curl.c vendored
View file

@ -12,18 +12,23 @@
/* Ensure libcurl headers and helpers are defined at file scope (not inside vm_run) */ /* Ensure libcurl headers and helpers are defined at file scope (not inside vm_run) */
#ifdef FUN_WITH_CURL #ifdef FUN_WITH_CURL
#include <curl/curl.h> #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) { static size_t fun_curl_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
size_t add = sz * nm; size_t add = sz * nm;
FunCurlBuf *b = (FunCurlBuf*)ud; FunCurlBuf *b = (FunCurlBuf *)ud;
char *p = (char*)realloc(b->d, b->n + add + 1); char *p = (char *)realloc(b->d, b->n + add + 1);
if (!p) return 0; if (!p) return 0;
memcpy(p + b->n, ptr, add); 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; return add;
} }
static size_t fun_curl_file_write_cb(void *ptr, size_t sz, size_t nm, void *ud) { 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); return fwrite(ptr, sz, nm, f);
} }
#endif #endif

22
src/external/ini.c vendored
View file

@ -11,18 +11,18 @@
#ifdef FUN_WITH_INI #ifdef FUN_WITH_INI
#if defined(__has_include) #if defined(__has_include)
# if __has_include(<iniparser/iniparser.h>) #if __has_include(<iniparser/iniparser.h>)
# include <iniparser/iniparser.h> #include <iniparser/dictionary.h>
# include <iniparser/dictionary.h> #include <iniparser/iniparser.h>
# elif __has_include(<iniparser.h>) #elif __has_include(<iniparser.h>)
# include <iniparser.h> #include <dictionary.h>
# include <dictionary.h> #include <iniparser.h>
# else
# error "iniparser headers not found"
# endif
#else #else
# include <iniparser/iniparser.h> #error "iniparser headers not found"
# include <iniparser/dictionary.h> #endif
#else
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif #endif
#include "vm/ini/handles.h" #include "vm/ini/handles.h"
#endif #endif

39
src/external/json.c vendored
View file

@ -9,7 +9,7 @@
* Added: 2025-12-11 (2025-12-11 migrated from src/vm.c) * 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 #ifdef FUN_WITH_JSON
#include "value.h" #include "value.h"
@ -23,24 +23,30 @@ static Value json_to_fun(json_object *j) {
if (!j) return make_nil(); if (!j) return make_nil();
enum json_type t = json_object_get_type(j); enum json_type t = json_object_get_type(j);
switch (t) { switch (t) {
case json_type_null: return make_nil(); case json_type_null:
case json_type_boolean: return make_bool(json_object_get_boolean(j)); return make_nil();
case json_type_double: return make_float(json_object_get_double(j)); case json_type_boolean:
case json_type_int: return make_int((int64_t)json_object_get_int64(j)); return make_bool(json_object_get_boolean(j));
case json_type_string: return make_string(json_object_get_string(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: { case json_type_array: {
size_t n = json_object_array_length(j); size_t n = json_object_array_length(j);
if (n == 0) { if (n == 0) {
return make_array_from_values(NULL, 0); return make_array_from_values(NULL, 0);
} }
Value *vals = (Value*)malloc(sizeof(Value) * n); Value *vals = (Value *)malloc(sizeof(Value) * n);
if (!vals) return make_array_from_values(NULL, 0); if (!vals) return make_array_from_values(NULL, 0);
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
json_object *item = json_object_array_get_idx(j, (int)i); json_object *item = json_object_array_get_idx(j, (int)i);
vals[i] = json_to_fun(item); vals[i] = json_to_fun(item);
} }
Value arr = make_array_from_values(vals, (int)n); Value arr = make_array_from_values(vals, (int)n);
for (size_t i = 0; i < n; ++i) free_value(vals[i]); for (size_t i = 0; i < n; ++i)
free_value(vals[i]);
free(vals); free(vals);
return arr; return arr;
} }
@ -56,13 +62,18 @@ static Value json_to_fun(json_object *j) {
} }
} }
static json_object* fun_to_json(const Value *v) { static json_object *fun_to_json(const Value *v) {
switch (v->type) { switch (v->type) {
case VAL_NIL: return json_object_new_null(); case VAL_NIL:
case VAL_BOOL: return json_object_new_boolean(v->i ? 1 : 0); return json_object_new_null();
case VAL_INT: return json_object_new_int64(v->i); case VAL_BOOL:
case VAL_FLOAT: return json_object_new_double(v->d); return json_object_new_boolean(v->i ? 1 : 0);
case VAL_STRING: return json_object_new_string(v->s ? v->s : ""); 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: { case VAL_ARRAY: {
json_object *arr = json_object_new_array(); json_object *arr = json_object_new_array();
int n = array_length(v); int n = array_length(v);

View file

@ -1,5 +1,5 @@
/* /*
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2026 Johannes Findeisen <you@hanez.org> * Copyright 2026 Johannes Findeisen <you@hanez.org>
@ -40,7 +40,7 @@ static char *fun_libressl_md5_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -49,18 +49,24 @@ static char *fun_libressl_md5_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif
@ -75,7 +81,7 @@ static char *fun_libressl_sha256_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -84,18 +90,24 @@ static char *fun_libressl_sha256_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif
@ -110,7 +122,7 @@ static char *fun_libressl_sha512_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -119,18 +131,24 @@ static char *fun_libressl_sha512_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif
@ -145,7 +163,7 @@ static char *fun_libressl_ripemd160_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -154,18 +172,24 @@ static char *fun_libressl_ripemd160_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif

11
src/external/libsql.c vendored
View file

@ -10,10 +10,10 @@
*/ */
#ifdef FUN_WITH_LIBSQL #ifdef FUN_WITH_LIBSQL
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
typedef struct LibSqlHandle { typedef struct LibSqlHandle {
int id; int id;
@ -25,7 +25,7 @@ static LibSqlHandle *g_libsql_handles = NULL;
static int g_libsql_next_id = 1; static int g_libsql_next_id = 1;
static LibSqlHandle *libsql_reg_add(sqlite3 *db) { static LibSqlHandle *libsql_reg_add(sqlite3 *db) {
LibSqlHandle *h = (LibSqlHandle*)malloc(sizeof(LibSqlHandle)); LibSqlHandle *h = (LibSqlHandle *)malloc(sizeof(LibSqlHandle));
if (!h) return NULL; if (!h) return NULL;
h->id = g_libsql_next_id++; h->id = g_libsql_next_id++;
h->db = db; h->db = db;
@ -36,7 +36,10 @@ static LibSqlHandle *libsql_reg_add(sqlite3 *db) {
static LibSqlHandle *libsql_reg_get(int id) { static LibSqlHandle *libsql_reg_get(int id) {
LibSqlHandle *p = g_libsql_handles; LibSqlHandle *p = g_libsql_handles;
while (p) { if (p->id == id) return p; p = p->next; } while (p) {
if (p->id == id) return p;
p = p->next;
}
return NULL; return NULL;
} }

View file

@ -1,5 +1,5 @@
/* /*
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2026 Johannes Findeisen <you@hanez.org> * Copyright 2026 Johannes Findeisen <you@hanez.org>
@ -9,7 +9,7 @@
* Added: 2026-02-19 * Added: 2026-02-19
*/ */
/* /*
* OpenSSL integration helpers (MD5, RIPEMD-160, SHA-256, SHA-512) * OpenSSL integration helpers (MD5, RIPEMD-160, SHA-256, SHA-512)
*/ */
@ -35,7 +35,7 @@ static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -45,18 +45,24 @@ static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif
@ -72,7 +78,7 @@ static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -81,18 +87,24 @@ static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif
@ -108,7 +120,7 @@ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -117,18 +129,24 @@ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif
@ -146,7 +164,7 @@ static char *fun_openssl_ripemd160_hex(const unsigned char *data, size_t len) {
if (!md) return NULL; if (!md) return NULL;
int dlen = EVP_MD_get_size(md); int dlen = EVP_MD_get_size(md);
if (dlen <= 0) return NULL; if (dlen <= 0) return NULL;
unsigned char *digest = (unsigned char*)malloc((size_t)dlen); unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
if (!digest) return NULL; if (!digest) return NULL;
unsigned int out_len = 0; unsigned int out_len = 0;
int ok; int ok;
@ -155,18 +173,24 @@ static char *fun_openssl_ripemd160_hex(const unsigned char *data, size_t len) {
} else { } else {
ok = EVP_Digest(data, len, digest, &out_len, md, NULL); ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
} }
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; } if (ok != 1 || (int)out_len != dlen) {
char *hex = (char*)malloc((size_t)dlen * 2 + 1); free(digest);
if (!hex) { free(digest); return NULL; } return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) {
free(digest);
return NULL;
}
for (int i = 0; i < dlen; ++i) { for (int i = 0; i < dlen; ++i) {
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF]; hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
hex[2*i+1] = hexdig[digest[i] & 0xF]; hex[2 * i + 1] = hexdig[digest[i] & 0xF];
} }
hex[dlen * 2] = '\0'; hex[dlen * 2] = '\0';
free(digest); free(digest);
return hex; return hex;
#else #else
char *hex = (char*)malloc(1); char *hex = (char *)malloc(1);
if (hex) hex[0] = '\0'; if (hex) hex[0] = '\0';
return hex; return hex;
#endif #endif

View file

@ -9,7 +9,7 @@
* Added: 2025-12-11 (2025-12-11 migrated from src/vm/libsql/common.c) * 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 * 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 * 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 * PCRE2_CODE_UNIT_WIDTH macro must be defined before the first inclusion of

55
src/external/pcsc.c vendored
View file

@ -16,24 +16,24 @@ Included the file scope from vm.c.
#ifdef FUN_WITH_PCSC #ifdef FUN_WITH_PCSC
#if defined(__has_include) #if defined(__has_include)
#if __has_include(<PCSC/winscard.h>) #if __has_include(<PCSC/winscard.h>)
#include <PCSC/winscard.h> #include <PCSC/winscard.h>
#include <PCSC/wintypes.h> #include <PCSC/wintypes.h>
#elif __has_include(<winscard.h>) #elif __has_include(<winscard.h>)
#include <winscard.h> #include <winscard.h>
#else #else
#error "FUN_WITH_PCSC is enabled but PCSC headers were not found" #error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
#endif #endif
#else #else
#include <PCSC/winscard.h> #include <PCSC/winscard.h>
#include <PCSC/wintypes.h> #include <PCSC/wintypes.h>
#endif #endif
#include <string.h> #include <string.h>
typedef struct { typedef struct {
SCARDCONTEXT ctx; SCARDCONTEXT ctx;
int in_use; int in_use;
} pcsc_ctx_entry; } pcsc_ctx_entry;
typedef struct { typedef struct {
SCARDHANDLE h; SCARDHANDLE h;
@ -45,31 +45,40 @@ static pcsc_ctx_entry g_pcsc_ctx[8];
static pcsc_card_entry g_pcsc_card[32]; static pcsc_card_entry g_pcsc_card[32];
static int pcsc_alloc_ctx_slot(void) { static int pcsc_alloc_ctx_slot(void) {
for (int i = 0; i < (int)(sizeof(g_pcsc_ctx)/sizeof(g_pcsc_ctx[0])); ++i) { 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; } 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) { static int pcsc_alloc_card_slot(void) {
for (int i = 0; i < (int)(sizeof(g_pcsc_card)/sizeof(g_pcsc_card[0])); ++i) { 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; } 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) { static pcsc_ctx_entry *pcsc_get_ctx(int id) {
if (id <= 0) return NULL; if (id <= 0) return NULL;
int idx = id - 1; int idx = id - 1;
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx)/sizeof(g_pcsc_ctx[0]))) return NULL; 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; if (!g_pcsc_ctx[idx].in_use) return NULL;
return &g_pcsc_ctx[idx]; return &g_pcsc_ctx[idx];
} }
static pcsc_card_entry* pcsc_get_card(int id) { static pcsc_card_entry *pcsc_get_card(int id) {
if (id <= 0) return NULL; if (id <= 0) return NULL;
int idx = id - 1; int idx = id - 1;
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card)/sizeof(g_pcsc_card[0]))) return NULL; 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; if (!g_pcsc_card[idx].in_use) return NULL;
return &g_pcsc_card[idx]; return &g_pcsc_card[idx];
} }

16
src/external/sqlite.c vendored
View file

@ -24,8 +24,8 @@ typedef struct SqlHandle {
static SqlHandle *g_sql_handles = NULL; static SqlHandle *g_sql_handles = NULL;
static int g_sql_next_id = 1; static int g_sql_next_id = 1;
static SqlHandle* sql_reg_add(sqlite3 *db) { static SqlHandle *sql_reg_add(sqlite3 *db) {
SqlHandle *h = (SqlHandle*)calloc(1, sizeof(SqlHandle)); SqlHandle *h = (SqlHandle *)calloc(1, sizeof(SqlHandle));
if (!h) return NULL; if (!h) return NULL;
h->id = g_sql_next_id++; h->id = g_sql_next_id++;
h->db = db; h->db = db;
@ -34,15 +34,21 @@ static SqlHandle* sql_reg_add(sqlite3 *db) {
return h; return h;
} }
static SqlHandle* sql_reg_get(int id) { static SqlHandle *sql_reg_get(int id) {
for (SqlHandle *p = g_sql_handles; p; p = p->next) if (p->id == id) return p; for (SqlHandle *p = g_sql_handles; p; p = p->next)
if (p->id == id) return p;
return NULL; return NULL;
} }
static void sql_reg_del(int id) { static void sql_reg_del(int id) {
SqlHandle **pp = &g_sql_handles; SqlHandle **pp = &g_sql_handles;
while (*pp) { while (*pp) {
if ((*pp)->id == id) { SqlHandle *d = *pp; *pp = d->next; free(d); return; } if ((*pp)->id == id) {
SqlHandle *d = *pp;
*pp = d->next;
free(d);
return;
}
pp = &(*pp)->next; pp = &(*pp)->next;
} }
} }

24
src/external/tcltk.c vendored
View file

@ -12,7 +12,7 @@
#ifdef FUN_WITH_TCLTK #ifdef FUN_WITH_TCLTK
#include <tcl.h> #include <tcl.h>
#include <tk.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) { static void fun_tk_init_once(void) {
if (g_fun_tcl_interp) return; if (g_fun_tcl_interp) return;
@ -37,7 +37,7 @@ static int fun_tk_eval_script(const char *script) {
return rc; /* TCL_OK = 0 */ return rc; /* TCL_OK = 0 */
} }
static const char* fun_tk_get_result(void) { static const char *fun_tk_get_result(void) {
fun_tk_init_once(); fun_tk_init_once();
if (!g_fun_tcl_interp) return ""; if (!g_fun_tcl_interp) return "";
return Tcl_GetStringResult(g_fun_tcl_interp); return Tcl_GetStringResult(g_fun_tcl_interp);
@ -48,7 +48,8 @@ static void fun_tk_loop(void) {
if (!g_fun_tcl_interp) return; if (!g_fun_tcl_interp) return;
/* Drive Tk event loop until all main windows are closed */ /* Drive Tk event loop until all main windows are closed */
while (Tk_GetNumMainWindows() > 0) { while (Tk_GetNumMainWindows() > 0) {
while (Tcl_DoOneEvent(0)) {} while (Tcl_DoOneEvent(0)) {
}
/* tiny sleep to avoid busy spin */ /* tiny sleep to avoid busy spin */
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
@ -62,8 +63,17 @@ static void fun_tk_loop(void) {
} }
#else #else
/* Stubs when Tcl/Tk is disabled */ /* Stubs when Tcl/Tk is disabled */
static void fun_tk_init_once(void) { (void)0; } static void fun_tk_init_once(void) {
static int fun_tk_eval_script(const char *script) { (void)script; return -1; } (void)0;
static const char* fun_tk_get_result(void) { return ""; } }
static void fun_tk_loop(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 #endif

34
src/external/xml2.c vendored
View file

@ -13,24 +13,34 @@
#include <libxml/parser.h> #include <libxml/parser.h>
#include <libxml/tree.h> #include <libxml/tree.h>
typedef struct { xmlDocPtr doc; int in_use; } XmlDocSlot; typedef struct {
typedef struct { xmlNodePtr node; int in_use; } XmlNodeSlot; xmlDocPtr doc;
int in_use;
} XmlDocSlot;
typedef struct {
xmlNodePtr node;
int in_use;
} XmlNodeSlot;
static XmlDocSlot g_xml_docs[64]; static XmlDocSlot g_xml_docs[64];
static XmlNodeSlot g_xml_nodes[256]; static XmlNodeSlot g_xml_nodes[256];
static int xml_doc_alloc(xmlDocPtr d) { static int xml_doc_alloc(xmlDocPtr d) {
for (int i = 1; i < (int)(sizeof(g_xml_docs)/sizeof(g_xml_docs[0])); ++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; } if (!g_xml_docs[i].in_use) {
g_xml_docs[i].in_use = 1;
g_xml_docs[i].doc = d;
return i;
}
} }
return 0; return 0;
} }
static xmlDocPtr xml_doc_get(int h) { 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; return NULL;
} }
static int xml_doc_free_handle(int h) { 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); if (g_xml_docs[h].doc) xmlFreeDoc(g_xml_docs[h].doc);
g_xml_docs[h].doc = NULL; g_xml_docs[h].doc = NULL;
g_xml_docs[h].in_use = 0; 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) { static int xml_node_alloc(xmlNodePtr n) {
for (int i = 1; i < (int)(sizeof(g_xml_nodes)/sizeof(g_xml_nodes[0])); ++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; } if (!g_xml_nodes[i].in_use) {
g_xml_nodes[i].in_use = 1;
g_xml_nodes[i].node = n;
return i;
}
} }
return 0; return 0;
} }
static xmlNodePtr xml_node_get(int h) { 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; return NULL;
} }
static int xml_node_free_handle(int h) { 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 */ /* nodes are owned by their document; do not free here */
g_xml_nodes[h].node = NULL; g_xml_nodes[h].node = NULL;
g_xml_nodes[h].in_use = 0; g_xml_nodes[h].in_use = 0;

View file

@ -1,5 +1,5 @@
/* /*
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -7,15 +7,15 @@
* https://opensource.org/license/apache-2-0 * https://opensource.org/license/apache-2-0
*/ */
/* /*
* Main entry point for the Fun language interpreter. * Main entry point for the Fun language interpreter.
* Builds a CLI that runs a script file if provided; otherwise starts the REPL * Builds a CLI that runs a script file if provided; otherwise starts the REPL
* when compiled with FUN_WITH_REPL enabled. * when compiled with FUN_WITH_REPL enabled.
*/ */
#include "bytecode.h" #include "bytecode.h"
#include "vm.h"
#include "parser.h" #include "parser.h"
#include "vm.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -119,7 +119,7 @@ int main(int argc, char **argv) {
for (int i = 0; i < sargc; ++i) { for (int i = 0; i < sargc; ++i) {
total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */ total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */
} }
char *joined = (char*)malloc(total); char *joined = (char *)malloc(total);
if (joined) { if (joined) {
joined[0] = '\0'; joined[0] = '\0';
for (int i = 0; i < sargc; ++i) { for (int i = 0; i < sargc; ++i) {

View file

@ -10,8 +10,8 @@
#include "bytecode.h" #include "bytecode.h"
#include "value.h" #include "value.h"
#include "vm.h" #include "vm.h"
#include <stdio.h>
#include <math.h> #include <math.h>
#include <stdio.h>
#define ASSERT_EQ(val, expected) \ #define ASSERT_EQ(val, expected) \
if ((val).type != VAL_INT || (val).i != (expected)) { \ if ((val).type != VAL_INT || (val).i != (expected)) { \

View file

@ -14,7 +14,7 @@
Value bi_enumerate(const Value *arr) { Value bi_enumerate(const Value *arr) {
int n = array_length(arr); int n = array_length(arr);
if (n <= 0) return make_array_from_values(NULL, 0); if (n <= 0) return make_array_from_values(NULL, 0);
Value *pairs = (Value*)malloc(sizeof(Value) * n); Value *pairs = (Value *)malloc(sizeof(Value) * n);
if (!pairs) return make_array_from_values(NULL, 0); if (!pairs) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
Value elem; Value elem;
@ -28,7 +28,8 @@ Value bi_enumerate(const Value *arr) {
pairs[i] = kv; pairs[i] = kv;
} }
Value out = make_array_from_values(pairs, n); Value out = make_array_from_values(pairs, n);
for (int i = 0; i < n; ++i) free_value(pairs[i]); for (int i = 0; i < n; ++i)
free_value(pairs[i]);
free(pairs); free(pairs);
return out; return out;
} }
@ -39,7 +40,7 @@ Value bi_zip(const Value *a, const Value *b) {
int nb = array_length(b); int nb = array_length(b);
int n = na < nb ? na : nb; int n = na < nb ? na : nb;
if (n <= 0) return make_array_from_values(NULL, 0); if (n <= 0) return make_array_from_values(NULL, 0);
Value *pairs = (Value*)malloc(sizeof(Value) * n); Value *pairs = (Value *)malloc(sizeof(Value) * n);
if (!pairs) return make_array_from_values(NULL, 0); if (!pairs) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
Value av, bv; Value av, bv;
@ -54,7 +55,8 @@ Value bi_zip(const Value *a, const Value *b) {
pairs[i] = kv; pairs[i] = kv;
} }
Value out = make_array_from_values(pairs, n); Value out = make_array_from_values(pairs, n);
for (int i = 0; i < n; ++i) free_value(pairs[i]); for (int i = 0; i < n; ++i)
free_value(pairs[i]);
free(pairs); free(pairs);
return out; return out;
} }

View file

@ -21,7 +21,7 @@ typedef struct Map {
} Map; } Map;
Value make_map_empty(void) { Value make_map_empty(void) {
Map *m = (Map*)malloc(sizeof(Map)); Map *m = (Map *)malloc(sizeof(Map));
if (!m) return make_nil(); if (!m) return make_nil();
m->refcount = 1; m->refcount = 1;
m->count = 0; m->count = 0;
@ -30,16 +30,17 @@ Value make_map_empty(void) {
m->vals = NULL; m->vals = NULL;
Value v; Value v;
v.type = VAL_MAP; v.type = VAL_MAP;
v.map = (struct Map*)m; v.map = (struct Map *)m;
return v; return v;
} }
static int map_ensure_cap(Map *m, int need) { static int map_ensure_cap(Map *m, int need) {
if (m->cap >= need) return 1; if (m->cap >= need) return 1;
int ncap = m->cap == 0 ? 4 : m->cap * 2; int ncap = m->cap == 0 ? 4 : m->cap * 2;
while (ncap < need) ncap *= 2; while (ncap < need)
char **nkeys = (char**)realloc(m->keys, sizeof(char*) * ncap); ncap *= 2;
Value *nvals = (Value*)realloc(m->vals, sizeof(Value) * ncap); char **nkeys = (char **)realloc(m->keys, sizeof(char *) * ncap);
Value *nvals = (Value *)realloc(m->vals, sizeof(Value) * ncap);
if (!nkeys || !nvals) return 0; if (!nkeys || !nvals) return 0;
m->keys = nkeys; m->keys = nkeys;
m->vals = nvals; m->vals = nvals;
@ -48,8 +49,11 @@ static int map_ensure_cap(Map *m, int need) {
} }
int map_set(Value *vm, const char *key, Value v) { int map_set(Value *vm, const char *key, Value v) {
if (!vm || vm->type != VAL_MAP || !vm->map || !key) { free_value(v); return 0; } if (!vm || vm->type != VAL_MAP || !vm->map || !key) {
Map *m = (Map*)vm->map; free_value(v);
return 0;
}
Map *m = (Map *)vm->map;
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) { if (strcmp(m->keys[i], key) == 0) {
free_value(m->vals[i]); free_value(m->vals[i]);
@ -57,7 +61,10 @@ int map_set(Value *vm, const char *key, Value v) {
return 1; return 1;
} }
} }
if (!map_ensure_cap(m, m->count + 1)) { free_value(v); return 0; } if (!map_ensure_cap(m, m->count + 1)) {
free_value(v);
return 0;
}
m->keys[m->count] = strdup(key); m->keys[m->count] = strdup(key);
m->vals[m->count] = v; m->vals[m->count] = v;
m->count++; m->count++;
@ -66,7 +73,7 @@ int map_set(Value *vm, const char *key, Value v) {
int map_get_copy(const Value *vm, const char *key, Value *out) { int map_get_copy(const Value *vm, const char *key, Value *out) {
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0; if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
Map *m = (Map*)vm->map; Map *m = (Map *)vm->map;
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) { if (strcmp(m->keys[i], key) == 0) {
if (out) *out = copy_value(&m->vals[i]); if (out) *out = copy_value(&m->vals[i]);
@ -78,7 +85,7 @@ int map_get_copy(const Value *vm, const char *key, Value *out) {
int map_has(const Value *vm, const char *key) { int map_has(const Value *vm, const char *key) {
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0; if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
Map *m = (Map*)vm->map; Map *m = (Map *)vm->map;
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
if (strcmp(m->keys[i], key) == 0) return 1; if (strcmp(m->keys[i], key) == 0) return 1;
} }
@ -87,30 +94,32 @@ int map_has(const Value *vm, const char *key) {
Value map_keys_array(const Value *vm) { Value map_keys_array(const Value *vm) {
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0); if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
Map *m = (Map*)vm->map; Map *m = (Map *)vm->map;
if (m->count <= 0) return make_array_from_values(NULL, 0); if (m->count <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * m->count); Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
if (!tmp) return make_array_from_values(NULL, 0); if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
tmp[i] = make_string(m->keys[i]); tmp[i] = make_string(m->keys[i]);
} }
Value arr = make_array_from_values(tmp, m->count); Value arr = make_array_from_values(tmp, m->count);
for (int i = 0; i < m->count; ++i) free_value(tmp[i]); for (int i = 0; i < m->count; ++i)
free_value(tmp[i]);
free(tmp); free(tmp);
return arr; return arr;
} }
Value map_values_array(const Value *vm) { Value map_values_array(const Value *vm) {
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0); if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
Map *m = (Map*)vm->map; Map *m = (Map *)vm->map;
if (m->count <= 0) return make_array_from_values(NULL, 0); if (m->count <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * m->count); Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
if (!tmp) return make_array_from_values(NULL, 0); if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
tmp[i] = copy_value(&m->vals[i]); tmp[i] = copy_value(&m->vals[i]);
} }
Value arr = make_array_from_values(tmp, m->count); Value arr = make_array_from_values(tmp, m->count);
for (int i = 0; i < m->count; ++i) free_value(tmp[i]); for (int i = 0; i < m->count; ++i)
free_value(tmp[i]);
free(tmp); free(tmp);
return arr; return arr;
} }

File diff suppressed because it is too large Load diff

View file

@ -7,21 +7,30 @@
* https://opensource.org/license/apache-2-0 * https://opensource.org/license/apache-2-0
*/ */
#include "parser.h"
#include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include "parser.h"
static char *read_file_all(const char *path, size_t *out_len) { static char *read_file_all(const char *path, size_t *out_len) {
FILE *f = fopen(path, "rb"); FILE *f = fopen(path, "rb");
if (!f) return NULL; if (!f) return NULL;
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; } if (fseek(f, 0, SEEK_END) != 0) {
fclose(f);
return NULL;
}
long sz = ftell(f); long sz = ftell(f);
if (sz < 0) { fclose(f); return NULL; } if (sz < 0) {
fclose(f);
return NULL;
}
rewind(f); rewind(f);
char *buf = (char*)malloc((size_t)sz + 1); char *buf = (char *)malloc((size_t)sz + 1);
if (!buf) { fclose(f); return NULL; } if (!buf) {
fclose(f);
return NULL;
}
size_t n = fread(buf, 1, (size_t)sz, f); size_t n = fread(buf, 1, (size_t)sz, f);
fclose(f); fclose(f);
buf[n] = '\0'; buf[n] = '\0';
@ -32,13 +41,17 @@ static char *read_file_all(const char *path, size_t *out_len) {
static void skip_ws(const char *src, size_t len, size_t *pos) { static void skip_ws(const char *src, size_t len, size_t *pos) {
while (*pos < len) { while (*pos < len) {
char c = src[*pos]; char c = src[*pos];
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { (*pos)++; continue; } if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
(*pos)++;
continue;
}
break; break;
} }
} }
static void skip_line(const char *src, size_t len, size_t *pos) { static void skip_line(const char *src, size_t len, size_t *pos) {
while (*pos < len && src[*pos] != '\n') (*pos)++; while (*pos < len && src[*pos] != '\n')
(*pos)++;
if (*pos < len && src[*pos] == '\n') (*pos)++; if (*pos < len && src[*pos] == '\n') (*pos)++;
} }
@ -52,7 +65,8 @@ static void skip_comments(const char *src, size_t len, size_t *pos) {
} }
if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '*') { if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '*') {
*pos += 2; *pos += 2;
while (*pos + 1 < len && !(src[*pos] == '*' && src[*pos + 1] == '/')) (*pos)++; while (*pos + 1 < len && !(src[*pos] == '*' && src[*pos + 1] == '/'))
(*pos)++;
if (*pos + 1 < len) *pos += 2; if (*pos + 1 < len) *pos += 2;
continue; continue;
} }
@ -76,14 +90,18 @@ static void skip_identifier(const char *src, size_t len, size_t *pos) {
size_t p = *pos; size_t p = *pos;
if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) { if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) {
p++; p++;
while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_')) p++; while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_'))
p++;
} }
*pos = p; *pos = p;
} }
static int consume_char(const char *src, size_t len, size_t *pos, char expected) { static int consume_char(const char *src, size_t len, size_t *pos, char expected) {
skip_ws(src, len, pos); skip_ws(src, len, pos);
if (*pos < len && src[*pos] == expected) { (*pos)++; return 1; } if (*pos < len && src[*pos] == expected) {
(*pos)++;
return 1;
}
return 0; return 0;
} }
@ -94,37 +112,60 @@ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t
if (quote != '"' && quote != '\'') return NULL; if (quote != '"' && quote != '\'') return NULL;
(*pos)++; // skip opening quote (*pos)++; // skip opening quote
size_t cap = 64, out_len = 0; size_t cap = 64, out_len = 0;
char *out = (char*)malloc(cap); char *out = (char *)malloc(cap);
if (!out) return NULL; if (!out) return NULL;
while (*pos < len) { while (*pos < len) {
char c = src[*pos]; char c = src[*pos];
if (c == quote) { (*pos)++; break; } if (c == quote) {
(*pos)++;
break;
}
if (c == '\\') { if (c == '\\') {
(*pos)++; (*pos)++;
if (*pos >= len) break; if (*pos >= len) break;
char e = src[*pos]; char e = src[*pos];
switch (e) { switch (e) {
case 'n': c = '\n'; break; case 'n':
case 'r': c = '\r'; break; c = '\n';
case 't': c = '\t'; break; break;
case '\\': c = '\\'; break; case 'r':
case '"': c = '"'; break; c = '\r';
case '\'': c = '\''; break; break;
default: c = e; break; case 't':
c = '\t';
break;
case '\\':
c = '\\';
break;
case '"':
c = '"';
break;
case '\'':
c = '\'';
break;
default:
c = e;
break;
} }
} }
if (out_len + 1 >= cap) { if (out_len + 1 >= cap) {
cap *= 2; cap *= 2;
char *tmp = (char*)realloc(out, cap); char *tmp = (char *)realloc(out, cap);
if (!tmp) { free(out); return NULL; } if (!tmp) {
free(out);
return NULL;
}
out = tmp; out = tmp;
} }
out[out_len++] = c; out[out_len++] = c;
(*pos)++; (*pos)++;
} }
if (out_len + 1 >= cap) { if (out_len + 1 >= cap) {
char *tmp = (char*)realloc(out, cap + 1); char *tmp = (char *)realloc(out, cap + 1);
if (!tmp) { free(out); return NULL; } if (!tmp) {
free(out);
return NULL;
}
out = tmp; out = tmp;
} }
out[out_len] = '\0'; out[out_len] = '\0';
@ -136,7 +177,10 @@ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t
static void skip_spaces(const char *src, size_t len, size_t *pos) { static void skip_spaces(const char *src, size_t len, size_t *pos) {
while (*pos < len) { while (*pos < len) {
char c = src[*pos]; char c = src[*pos];
if (c == ' ' || c == '\t' || c == '\r') { (*pos)++; continue; } if (c == ' ' || c == '\t' || c == '\r') {
(*pos)++;
continue;
}
break; break;
} }
} }
@ -146,9 +190,10 @@ static int read_identifier_into(const char *src, size_t len, size_t *pos, char *
if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) { if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) {
size_t start = p; size_t start = p;
p++; p++;
while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_')) p++; while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_'))
p++;
size_t n = p - start; size_t n = p - start;
char *name = (char*)malloc(n + 1); char *name = (char *)malloc(n + 1);
if (!name) return 0; if (!name) return 0;
memcpy(name, src + start, n); memcpy(name, src + start, n);
name[n] = '\0'; name[n] = '\0';
@ -167,12 +212,18 @@ static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos
if (src[p] == '-') sign = -1; if (src[p] == '-') sign = -1;
p++; p++;
} }
if (p >= len) { *ok = 0; return 0; } if (p >= len) {
*ok = 0;
return 0;
}
/* Hexadecimal: 0x... or 0X... */ /* Hexadecimal: 0x... or 0X... */
if ((p + 1) < len && src[p] == '0' && (src[p + 1] == 'x' || src[p + 1] == 'X')) { if ((p + 1) < len && src[p] == '0' && (src[p + 1] == 'x' || src[p + 1] == 'X')) {
p += 2; p += 2;
if (p >= len || !isxdigit((unsigned char)src[p])) { *ok = 0; return 0; } if (p >= len || !isxdigit((unsigned char)src[p])) {
*ok = 0;
return 0;
}
uint64_t val = 0; uint64_t val = 0;
while (p < len && isxdigit((unsigned char)src[p])) { while (p < len && isxdigit((unsigned char)src[p])) {
char c = src[p]; char c = src[p];
@ -189,7 +240,10 @@ static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos
} }
/* Decimal fallback */ /* Decimal fallback */
if (!isdigit((unsigned char)src[p])) { *ok = 0; return 0; } if (!isdigit((unsigned char)src[p])) {
*ok = 0;
return 0;
}
uint64_t val = 0; uint64_t val = 0;
while (p < len && isdigit((unsigned char)src[p])) { while (p < len && isdigit((unsigned char)src[p])) {
val = val * 10 + (uint64_t)(src[p] - '0'); val = val * 10 + (uint64_t)(src[p] - '0');
@ -220,7 +274,7 @@ typedef struct {
} StrBuf; } StrBuf;
static void sb_init(StrBuf *sb) { static void sb_init(StrBuf *sb) {
sb->buf = (char*)malloc(256); sb->buf = (char *)malloc(256);
sb->cap = sb->buf ? 256 : 0; sb->cap = sb->buf ? 256 : 0;
sb->len = 0; sb->len = 0;
if (sb->buf) sb->buf[0] = '\0'; if (sb->buf) sb->buf[0] = '\0';
@ -229,8 +283,9 @@ static void sb_init(StrBuf *sb) {
static void sb_reserve(StrBuf *sb, size_t need) { static void sb_reserve(StrBuf *sb, size_t need) {
if (need <= sb->cap) return; if (need <= sb->cap) return;
size_t nc = sb->cap ? sb->cap : 256; size_t nc = sb->cap ? sb->cap : 256;
while (nc < need) nc *= 2; while (nc < need)
char *nb = (char*)xrealloc(sb->buf, nc); nc *= 2;
char *nb = (char *)xrealloc(sb->buf, nc);
if (!nb) return; if (!nb) return;
sb->buf = nb; sb->buf = nb;
sb->cap = nc; sb->cap = nc;
@ -273,7 +328,7 @@ static void nl_add(NameList *nl, const char *name) {
if (!name || !name[0]) return; if (!name || !name[0]) return;
if (nl->count >= nl->cap) { if (nl->count >= nl->cap) {
int ncap = nl->cap ? nl->cap * 2 : 8; int ncap = nl->cap ? nl->cap * 2 : 8;
char **nn = (char**)realloc(nl->names, (size_t)ncap * sizeof(char*)); char **nn = (char **)realloc(nl->names, (size_t)ncap * sizeof(char *));
if (!nn) return; if (!nn) return;
nl->names = nn; nl->names = nn;
nl->cap = ncap; nl->cap = ncap;
@ -283,7 +338,8 @@ static void nl_add(NameList *nl, const char *name) {
static void nl_free(NameList *nl) { static void nl_free(NameList *nl) {
if (!nl) return; if (!nl) return;
for (int i = 0; i < nl->count; ++i) free(nl->names[i]); for (int i = 0; i < nl->count; ++i)
free(nl->names[i]);
free(nl->names); free(nl->names);
nl->names = NULL; nl->names = NULL;
nl->count = nl->cap = 0; nl->count = nl->cap = 0;
@ -296,12 +352,16 @@ static void collect_exports_top_level(const char *text, NameList *out) {
size_t len = strlen(text); size_t len = strlen(text);
int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0; int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0;
int bol = 1; int bol = 1;
for (size_t i = 0; i < len; ) { for (size_t i = 0; i < len;) {
char c = text[i]; char c = text[i];
if (in_line) { if (in_line) {
if (c == '\n') { in_line = 0; bol = 1; } if (c == '\n') {
else { bol = 0; } in_line = 0;
bol = 1;
} else {
bol = 0;
}
i++; i++;
continue; continue;
} }
@ -317,16 +377,30 @@ static void collect_exports_top_level(const char *text, NameList *out) {
continue; continue;
} }
if (in_sq) { if (in_sq) {
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; } if (!esc && c == '\\') {
if (!esc && c == '\'') { in_sq = 0; } esc = 1;
i++;
bol = 0;
continue;
}
if (!esc && c == '\'') {
in_sq = 0;
}
esc = 0; esc = 0;
bol = (c == '\n'); bol = (c == '\n');
i++; i++;
continue; continue;
} }
if (in_dq) { if (in_dq) {
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; } if (!esc && c == '\\') {
if (!esc && c == '"') { in_dq = 0; } esc = 1;
i++;
bol = 0;
continue;
}
if (!esc && c == '"') {
in_dq = 0;
}
esc = 0; esc = 0;
bol = (c == '\n'); bol = (c == '\n');
i++; i++;
@ -345,14 +419,27 @@ static void collect_exports_top_level(const char *text, NameList *out) {
i += 2; i += 2;
continue; continue;
} }
if (c == '\'') { in_sq = 1; bol = 0; i++; continue; } if (c == '\'') {
if (c == '"') { in_dq = 1; bol = 0; i++; continue; } in_sq = 1;
bol = 0;
i++;
continue;
}
if (c == '"') {
in_dq = 1;
bol = 0;
i++;
continue;
}
if (bol) { if (bol) {
/* Compute leading spaces to filter out indented constructs */ /* Compute leading spaces to filter out indented constructs */
size_t j = i; size_t j = i;
int spaces = 0; int spaces = 0;
while (j < len && text[j] == ' ') { spaces++; j++; } while (j < len && text[j] == ' ') {
spaces++;
j++;
}
if (j < len && text[j] == '\t') { if (j < len && text[j] == '\t') {
/* tabs not allowed for indentation; treat as not top-level */ /* tabs not allowed for indentation; treat as not top-level */
bol = 0; bol = 0;
@ -366,12 +453,14 @@ static void collect_exports_top_level(const char *text, NameList *out) {
const char *kw2 = "class"; const char *kw2 = "class";
if (j + 3 <= len && strncmp(text + j, kw1, 3) == 0 && (j + 3 == len || isspace((unsigned char)text[j + 3]))) { if (j + 3 <= len && strncmp(text + j, kw1, 3) == 0 && (j + 3 == len || isspace((unsigned char)text[j + 3]))) {
size_t p = j + 3; size_t p = j + 3;
while (p < len && (text[p] == ' ' || text[p] == '\t')) p++; while (p < len && (text[p] == ' ' || text[p] == '\t'))
p++;
/* read identifier */ /* read identifier */
size_t start = p; size_t start = p;
if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) { if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) {
p++; p++;
while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_')) p++; while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_'))
p++;
size_t n = p - start; size_t n = p - start;
if (n > 0) { if (n > 0) {
char tmp[256]; char tmp[256];
@ -383,12 +472,14 @@ static void collect_exports_top_level(const char *text, NameList *out) {
} }
} else if (j + 5 <= len && strncmp(text + j, kw2, 5) == 0 && (j + 5 == len || isspace((unsigned char)text[j + 5]))) { } else if (j + 5 <= len && strncmp(text + j, kw2, 5) == 0 && (j + 5 == len || isspace((unsigned char)text[j + 5]))) {
size_t p = j + 5; size_t p = j + 5;
while (p < len && (text[p] == ' ' || text[p] == '\t')) p++; while (p < len && (text[p] == ' ' || text[p] == '\t'))
p++;
/* read identifier */ /* read identifier */
size_t start = p; size_t start = p;
if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) { if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) {
p++; p++;
while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_')) p++; while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_'))
p++;
size_t n = p - start; size_t n = p - start;
if (n > 0) { if (n > 0) {
char tmp[256]; char tmp[256];
@ -415,10 +506,10 @@ static char *preprocess_includes_internal(const char *src, int depth) {
return strdup(""); return strdup("");
} }
/* Build-time default, can be overridden by compiler define -DDEFAULT_LIB_DIR=".../" */ /* Build-time default, can be overridden by compiler define -DDEFAULT_LIB_DIR=".../" */
#ifndef DEFAULT_LIB_DIR #ifndef DEFAULT_LIB_DIR
#define DEFAULT_LIB_DIR "/usr/share/fun/lib/" #define DEFAULT_LIB_DIR "/usr/share/fun/lib/"
#endif #endif
const char *env_lib = getenv("FUN_LIB_DIR"); const char *env_lib = getenv("FUN_LIB_DIR");
size_t len = strlen(src); size_t len = strlen(src);
@ -427,14 +518,15 @@ static char *preprocess_includes_internal(const char *src, int depth) {
int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0; int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0;
int bol = 1; /* beginning of line */ int bol = 1; /* beginning of line */
for (size_t i = 0; i < len; ) { for (size_t i = 0; i < len;) {
char c = src[i]; char c = src[i];
/* Detect include directive at BOL, outside comments/strings */ /* Detect include directive at BOL, outside comments/strings */
if (bol && !in_block && !in_sq && !in_dq) { if (bol && !in_block && !in_sq && !in_dq) {
size_t j = i; size_t j = i;
/* skip leading spaces/tabs */ /* skip leading spaces/tabs */
while (j < len && (src[j] == ' ' || src[j] == '\t')) j++; while (j < len && (src[j] == ' ' || src[j] == '\t'))
j++;
size_t k = j; size_t k = j;
if (k < len && src[k] == '#') k++; if (k < len && src[k] == '#') k++;
const char *kw = "include"; const char *kw = "include";
@ -442,36 +534,42 @@ static char *preprocess_includes_internal(const char *src, int depth) {
if (k + kwlen <= len && strncmp(src + k, kw, kwlen) == 0) { if (k + kwlen <= len && strncmp(src + k, kw, kwlen) == 0) {
k += kwlen; k += kwlen;
/* next must be space/tab or delimiter */ /* next must be space/tab or delimiter */
while (k < len && (src[k] == ' ' || src[k] == '\t')) k++; while (k < len && (src[k] == ' ' || src[k] == '\t'))
k++;
if (k < len && (src[k] == '"' || src[k] == '<')) { if (k < len && (src[k] == '"' || src[k] == '<')) {
char opener = src[k]; char opener = src[k];
char closer = (opener == '"') ? '"' : '>'; char closer = (opener == '"') ? '"' : '>';
k++; k++;
size_t path_start = k; size_t path_start = k;
while (k < len && src[k] != closer) k++; while (k < len && src[k] != closer)
k++;
if (k < len && src[k] == closer) { if (k < len && src[k] == closer) {
size_t path_len = k - path_start; size_t path_len = k - path_start;
char *path = (char*)malloc(path_len + 1); char *path = (char *)malloc(path_len + 1);
if (path) { if (path) {
memcpy(path, src + path_start, path_len); memcpy(path, src + path_start, path_len);
path[path_len] = '\0'; path[path_len] = '\0';
/* parse optional 'as <alias>' then advance to end of line */ /* parse optional 'as <alias>' then advance to end of line */
k++; k++;
char ns[64]; ns[0] = '\0'; char ns[64];
ns[0] = '\0';
/* skip spaces/tabs */ /* skip spaces/tabs */
size_t ap = k; size_t ap = k;
while (ap < len && (src[ap] == ' ' || src[ap] == '\t')) ap++; while (ap < len && (src[ap] == ' ' || src[ap] == '\t'))
ap++;
/* optional 'as' */ /* optional 'as' */
const char *askw = "as"; const char *askw = "as";
if (ap + 2 <= len && strncmp(src + ap, askw, 2) == 0 && (ap + 2 == len || isspace((unsigned char)src[ap + 2]))) { if (ap + 2 <= len && strncmp(src + ap, askw, 2) == 0 && (ap + 2 == len || isspace((unsigned char)src[ap + 2]))) {
ap += 2; ap += 2;
while (ap < len && (src[ap] == ' ' || src[ap] == '\t')) ap++; while (ap < len && (src[ap] == ' ' || src[ap] == '\t'))
ap++;
/* read identifier [A-Za-z_][A-Za-z0-9_]* */ /* read identifier [A-Za-z_][A-Za-z0-9_]* */
size_t start = ap; size_t start = ap;
if (ap < len && (isalpha((unsigned char)src[ap]) || src[ap] == '_')) { if (ap < len && (isalpha((unsigned char)src[ap]) || src[ap] == '_')) {
ap++; ap++;
while (ap < len && (isalnum((unsigned char)src[ap]) || src[ap] == '_')) ap++; while (ap < len && (isalnum((unsigned char)src[ap]) || src[ap] == '_'))
ap++;
size_t n = ap - start; size_t n = ap - start;
size_t copy = (n < sizeof(ns) - 1) ? n : (sizeof(ns) - 1); size_t copy = (n < sizeof(ns) - 1) ? n : (sizeof(ns) - 1);
memcpy(ns, src + start, copy); memcpy(ns, src + start, copy);
@ -481,7 +579,8 @@ static char *preprocess_includes_internal(const char *src, int depth) {
} }
/* advance to end of line */ /* advance to end of line */
k = ap; k = ap;
while (k < len && src[k] != '\n') k++; while (k < len && src[k] != '\n')
k++;
if (k < len && src[k] == '\n') k++; if (k < len && src[k] == '\n') k++;
/* resolve file path; for <...> try FUN_LIB_DIR first, then default */ /* resolve file path; for <...> try FUN_LIB_DIR first, then default */
@ -547,9 +646,14 @@ static char *preprocess_includes_internal(const char *src, int depth) {
if (startp[0] == '#' && startp[1] == '!') { if (startp[0] == '#' && startp[1] == '!') {
/* skip until end of line, handling CR, LF, CRLF */ /* skip until end of line, handling CR, LF, CRLF */
const char *q = startp; const char *q = startp;
while (*q && *q != '\n' && *q != '\r') q++; while (*q && *q != '\n' && *q != '\r')
if (*q == '\r') { q++; if (*q == '\n') q++; } q++;
else if (*q == '\n') { q++; } if (*q == '\r') {
q++;
if (*q == '\n') q++;
} else if (*q == '\n') {
q++;
}
startp = q; startp = q;
} }
char *inc_clean = strdup(startp); char *inc_clean = strdup(startp);
@ -584,7 +688,8 @@ static char *preprocess_includes_internal(const char *src, int depth) {
/* If alias is present, export top-level fun/class into alias map */ /* If alias is present, export top-level fun/class into alias map */
if (ns[0] != '\0') { if (ns[0] != '\0') {
NameList nl; nl_init(&nl); NameList nl;
nl_init(&nl);
collect_exports_top_level(exp, &nl); collect_exports_top_level(exp, &nl);
for (int ei = 0; ei < nl.count; ++ei) { for (int ei = 0; ei < nl.count; ++ei) {
sb_append(&out, ns); sb_append(&out, ns);
@ -614,7 +719,12 @@ static char *preprocess_includes_internal(const char *src, int depth) {
/* normal stateful copy with comment/string tracking */ /* normal stateful copy with comment/string tracking */
if (in_line) { if (in_line) {
sb_append_ch(&out, c); sb_append_ch(&out, c);
if (c == '\n') { in_line = 0; bol = 1; } else { bol = 0; } if (c == '\n') {
in_line = 0;
bol = 1;
} else {
bol = 0;
}
i++; i++;
continue; continue;
} }
@ -633,8 +743,15 @@ static char *preprocess_includes_internal(const char *src, int depth) {
} }
if (in_sq) { if (in_sq) {
sb_append_ch(&out, c); sb_append_ch(&out, c);
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; } if (!esc && c == '\\') {
if (!esc && c == '\'') { in_sq = 0; } esc = 1;
i++;
bol = 0;
continue;
}
if (!esc && c == '\'') {
in_sq = 0;
}
esc = 0; esc = 0;
bol = (c == '\n') ? 1 : 0; bol = (c == '\n') ? 1 : 0;
i++; i++;
@ -642,8 +759,15 @@ static char *preprocess_includes_internal(const char *src, int depth) {
} }
if (in_dq) { if (in_dq) {
sb_append_ch(&out, c); sb_append_ch(&out, c);
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; } if (!esc && c == '\\') {
if (!esc && c == '"') { in_dq = 0; } esc = 1;
i++;
bol = 0;
continue;
}
if (!esc && c == '"') {
in_dq = 0;
}
esc = 0; esc = 0;
bol = (c == '\n') ? 1 : 0; bol = (c == '\n') ? 1 : 0;
i++; i++;
@ -698,7 +822,6 @@ char *preprocess_includes(const char *src) {
return preprocess_includes_internal(src, 0); return preprocess_includes_internal(src, 0);
} }
/* Float literal parser: supports decimal and scientific notation. Returns parsed double and advances pos on success. */ /* Float literal parser: supports decimal and scientific notation. Returns parsed double and advances pos on success. */
static double parse_float_literal_value(const char *src, size_t len, size_t *pos, int *ok) { static double parse_float_literal_value(const char *src, size_t len, size_t *pos, int *ok) {
size_t p = *pos; size_t p = *pos;
@ -712,13 +835,19 @@ static double parse_float_literal_value(const char *src, size_t len, size_t *pos
if (p < len && (src[p] == '+' || src[p] == '-')) p++; if (p < len && (src[p] == '+' || src[p] == '-')) p++;
/* integer part */ /* integer part */
while (p < len && isdigit((unsigned char)src[p])) { p++; saw_digit = 1; } while (p < len && isdigit((unsigned char)src[p])) {
p++;
saw_digit = 1;
}
/* fractional part */ /* fractional part */
if (p < len && src[p] == '.') { if (p < len && src[p] == '.') {
saw_dot = 1; saw_dot = 1;
p++; p++;
while (p < len && isdigit((unsigned char)src[p])) { p++; saw_digit = 1; } while (p < len && isdigit((unsigned char)src[p])) {
p++;
saw_digit = 1;
}
} }
/* exponent part */ /* exponent part */
@ -727,28 +856,39 @@ static double parse_float_literal_value(const char *src, size_t len, size_t *pos
size_t epos = p + 1; size_t epos = p + 1;
if (epos < len && (src[epos] == '+' || src[epos] == '-')) epos++; if (epos < len && (src[epos] == '+' || src[epos] == '-')) epos++;
size_t digits_start = epos; size_t digits_start = epos;
while (epos < len && isdigit((unsigned char)src[epos])) { epos++; } while (epos < len && isdigit((unsigned char)src[epos])) {
epos++;
}
if (epos == digits_start) { if (epos == digits_start) {
/* no digits after exponent -> not a float */ /* no digits after exponent -> not a float */
*ok = 0; return 0.0; *ok = 0;
return 0.0;
} }
p = epos; p = epos;
} }
if (!saw_digit || (!saw_dot && !saw_exp)) { if (!saw_digit || (!saw_dot && !saw_exp)) {
*ok = 0; return 0.0; *ok = 0;
return 0.0;
} }
/* Create temporary buffer to parse with strtod safely */ /* Create temporary buffer to parse with strtod safely */
size_t n = p - start; size_t n = p - start;
char *tmp = (char*)malloc(n + 1); char *tmp = (char *)malloc(n + 1);
if (!tmp) { *ok = 0; return 0.0; } if (!tmp) {
*ok = 0;
return 0.0;
}
memcpy(tmp, src + start, n); memcpy(tmp, src + start, n);
tmp[n] = '\0'; tmp[n] = '\0';
char *endp = NULL; char *endp = NULL;
double dv = strtod(tmp, &endp); double dv = strtod(tmp, &endp);
if (!endp || *endp != '\0') { free(tmp); *ok = 0; return 0.0; } if (!endp || *endp != '\0') {
free(tmp);
*ok = 0;
return 0.0;
}
*pos = p; *pos = p;
*ok = 1; *ok = 1;

File diff suppressed because it is too large Load diff

View file

@ -20,7 +20,7 @@ char *string_substr(const char *s, int start, int len) {
if (start > n) start = n; if (start > n) start = n;
if (len < 0) len = 0; if (len < 0) len = 0;
if (start + len > n) len = n - start; if (start + len > n) len = n - start;
char *out = (char*)malloc((size_t)len + 1); char *out = (char *)malloc((size_t)len + 1);
if (!out) return strdup(""); if (!out) return strdup("");
memcpy(out, s + start, (size_t)len); memcpy(out, s + start, (size_t)len);
out[len] = '\0'; out[len] = '\0';
@ -42,14 +42,15 @@ Value string_split_to_array(const char *s, const char *sep) {
/* split into characters */ /* split into characters */
int n = (int)strlen(s); int n = (int)strlen(s);
if (n <= 0) return make_array_from_values(NULL, 0); if (n <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * n); Value *tmp = (Value *)malloc(sizeof(Value) * n);
if (!tmp) return make_array_from_values(NULL, 0); if (!tmp) return make_array_from_values(NULL, 0);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
char ch[2] = { s[i], 0 }; char ch[2] = {s[i], 0};
tmp[i] = make_string(ch); tmp[i] = make_string(ch);
} }
Value arr = make_array_from_values(tmp, n); Value arr = make_array_from_values(tmp, n);
for (int i = 0; i < n; ++i) free_value(tmp[i]); for (int i = 0; i < n; ++i)
free_value(tmp[i]);
free(tmp); free(tmp);
return arr; return arr;
} }
@ -61,13 +62,13 @@ Value string_split_to_array(const char *s, const char *sep) {
const char *pos = NULL; const char *pos = NULL;
while ((pos = strstr(cur, sep)) != NULL) { while ((pos = strstr(cur, sep)) != NULL) {
int len = (int)(pos - cur); int len = (int)(pos - cur);
char *piece = (char*)malloc((size_t)len + 1); char *piece = (char *)malloc((size_t)len + 1);
if (!piece) break; if (!piece) break;
memcpy(piece, cur, (size_t)len); memcpy(piece, cur, (size_t)len);
piece[len] = '\0'; piece[len] = '\0';
if (count >= cap) { if (count >= cap) {
cap = cap == 0 ? 4 : cap * 2; cap = cap == 0 ? 4 : cap * 2;
parts = (Value*)realloc(parts, sizeof(Value) * cap); parts = (Value *)realloc(parts, sizeof(Value) * cap);
} }
parts[count++] = make_string(piece); parts[count++] = make_string(piece);
free(piece); free(piece);
@ -77,13 +78,14 @@ Value string_split_to_array(const char *s, const char *sep) {
char *tail = strdup(cur ? cur : ""); char *tail = strdup(cur ? cur : "");
if (count >= cap) { if (count >= cap) {
cap = cap == 0 ? 1 : cap + 1; cap = cap == 0 ? 1 : cap + 1;
parts = (Value*)realloc(parts, sizeof(Value) * cap); parts = (Value *)realloc(parts, sizeof(Value) * cap);
} }
parts[count++] = make_string(tail ? tail : ""); parts[count++] = make_string(tail ? tail : "");
free(tail); free(tail);
Value arr = make_array_from_values(parts, count); Value arr = make_array_from_values(parts, count);
for (int i = 0; i < count; ++i) free_value(parts[i]); for (int i = 0; i < count; ++i)
free_value(parts[i]);
free(parts); free(parts);
return arr; return arr;
} }
@ -95,7 +97,7 @@ char *array_join_with_sep(const Value *v, const char *sep) {
const int n = array_length(v); const int n = array_length(v);
if (n <= 0) return strdup(""); if (n <= 0) return strdup("");
char **parts = (char**)malloc(sizeof(char*) * n); char **parts = (char **)malloc(sizeof(char *) * n);
if (!parts) return strdup(""); if (!parts) return strdup("");
size_t total = 0; size_t total = 0;
@ -111,9 +113,10 @@ char *array_join_with_sep(const Value *v, const char *sep) {
if (i + 1 < n) total += strlen(sep); if (i + 1 < n) total += strlen(sep);
} }
char *out = (char*)malloc(total + 1); char *out = (char *)malloc(total + 1);
if (!out) { if (!out) {
for (int i = 0; i < n; ++i) free(parts[i]); for (int i = 0; i < n; ++i)
free(parts[i]);
free(parts); free(parts);
return strdup(""); return strdup("");
} }
@ -121,10 +124,12 @@ char *array_join_with_sep(const Value *v, const char *sep) {
size_t off = 0; size_t off = 0;
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
size_t li = strlen(parts[i]); size_t li = strlen(parts[i]);
memcpy(out + off, parts[i], li); off += li; memcpy(out + off, parts[i], li);
off += li;
if (i + 1 < n) { if (i + 1 < n) {
size_t ls = strlen(sep); size_t ls = strlen(sep);
memcpy(out + off, sep, ls); off += ls; memcpy(out + off, sep, ls);
off += ls;
} }
free(parts[i]); free(parts[i]);
} }

View file

@ -7,9 +7,9 @@
* https://opensource.org/license/apache-2-0 * https://opensource.org/license/apache-2-0
*/ */
#include "vm.h"
#include "bytecode.h" #include "bytecode.h"
#include "value.h" #include "value.h"
#include "vm.h"
#include <stdio.h> #include <stdio.h>
int main() { int main() {
@ -49,7 +49,7 @@ int main() {
/* --- Rust FFI demo: call a Rust opcode and string function --- */ /* --- Rust FFI demo: call a Rust opcode and string function --- */
#ifdef FUN_WITH_RUST #ifdef FUN_WITH_RUST
extern int fun_op_radd(VM *vm); extern int fun_op_radd(VM * vm);
extern const char *fun_rust_get_string(void); extern const char *fun_rust_get_string(void);
printf("=== Rust FFI demo ===\n"); printf("=== Rust FFI demo ===\n");

View file

@ -8,13 +8,13 @@
*/ */
#include "value.h" #include "value.h"
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h>
/* Compile helper implementations into this TU to avoid build system changes */ /* Compile helper implementations into this TU to avoid build system changes */
#include "str_utils.c"
#include "array_utils.c" #include "array_utils.c"
#include "str_utils.c"
typedef struct Array { typedef struct Array {
int refcount; int refcount;
@ -54,8 +54,10 @@ Value make_bool(int v) {
Value make_string(const char *s) { Value make_string(const char *s) {
Value val; Value val;
val.type = VAL_STRING; val.type = VAL_STRING;
if (s) val.s = strdup(s); if (s)
else val.s = strdup(""); val.s = strdup(s);
else
val.s = strdup("");
return val; return val;
} }
@ -72,10 +74,9 @@ Value make_nil(void) {
return v; return v;
} }
Value make_array_from_values(const Value *vals, int count) { Value make_array_from_values(const Value *vals, int count) {
if (count < 0) count = 0; if (count < 0) count = 0;
Array *arr = (Array*)malloc(sizeof(Array)); Array *arr = (Array *)malloc(sizeof(Array));
if (!arr) { if (!arr) {
Value nil = make_nil(); Value nil = make_nil();
return nil; return nil;
@ -83,7 +84,7 @@ Value make_array_from_values(const Value *vals, int count) {
arr->refcount = 1; arr->refcount = 1;
arr->count = count; arr->count = count;
if (count > 0) { if (count > 0) {
arr->items = (Value*)malloc(sizeof(Value) * count); arr->items = (Value *)malloc(sizeof(Value) * count);
if (!arr->items) { if (!arr->items) {
free(arr); free(arr);
Value nil = make_nil(); Value nil = make_nil();
@ -97,19 +98,19 @@ Value make_array_from_values(const Value *vals, int count) {
} }
Value v; Value v;
v.type = VAL_ARRAY; v.type = VAL_ARRAY;
v.arr = (struct Array*)arr; v.arr = (struct Array *)arr;
return v; return v;
} }
int array_length(const Value *v) { int array_length(const Value *v) {
if (!v || v->type != VAL_ARRAY || !v->arr) return -1; if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
const Array *a = (const Array*)v->arr; const Array *a = (const Array *)v->arr;
return a->count; return a->count;
} }
int array_get_copy(const Value *v, int index, Value *out) { int array_get_copy(const Value *v, int index, Value *out) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0; if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
const Array *a = (const Array*)v->arr; const Array *a = (const Array *)v->arr;
if (index < 0 || index >= a->count) return 0; if (index < 0 || index >= a->count) return 0;
if (out) *out = copy_value(&a->items[index]); if (out) *out = copy_value(&a->items[index]);
return 1; return 1;
@ -117,7 +118,7 @@ int array_get_copy(const Value *v, int index, Value *out) {
int array_set(Value *v, int index, Value newElem) { int array_set(Value *v, int index, Value newElem) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0; if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array*)v->arr; Array *a = (Array *)v->arr;
if (index < 0 || index >= a->count) return 0; if (index < 0 || index >= a->count) return 0;
free_value(a->items[index]); free_value(a->items[index]);
a->items[index] = newElem; /* take ownership */ a->items[index] = newElem; /* take ownership */
@ -130,8 +131,9 @@ static int ensure_array_capacity(Array *a, int newCount) {
int curr = a->count; int curr = a->count;
int cap = curr; int cap = curr;
if (cap < 4) cap = 4; if (cap < 4) cap = 4;
while (cap < newCount) cap *= 2; while (cap < newCount)
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * cap); cap *= 2;
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * cap);
if (!newItems) return 0; if (!newItems) return 0;
/* if growing beyond current count, initialize new slots to nil */ /* if growing beyond current count, initialize new slots to nil */
if (cap > a->count) { if (cap > a->count) {
@ -145,10 +147,13 @@ static int ensure_array_capacity(Array *a, int newCount) {
int array_push(Value *v, Value newElem) { int array_push(Value *v, Value newElem) {
if (!v || v->type != VAL_ARRAY || !v->arr) return -1; if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
Array *a = (Array*)v->arr; Array *a = (Array *)v->arr;
/* ensure capacity for count+1 by reallocating items array to at least count+1 elements */ /* 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)); Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
if (!newItems) { free_value(newElem); return -1; } if (!newItems) {
free_value(newElem);
return -1;
}
a->items = newItems; a->items = newItems;
a->items[a->count] = newElem; /* take ownership */ a->items[a->count] = newElem; /* take ownership */
a->count += 1; a->count += 1;
@ -157,22 +162,27 @@ int array_push(Value *v, Value newElem) {
int array_pop(Value *v, Value *out) { int array_pop(Value *v, Value *out) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0; if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array*)v->arr; Array *a = (Array *)v->arr;
if (a->count <= 0) return 0; if (a->count <= 0) return 0;
int idx = a->count - 1; int idx = a->count - 1;
if (out) *out = a->items[idx]; /* transfer ownership */ if (out)
else free_value(a->items[idx]); *out = a->items[idx]; /* transfer ownership */
else
free_value(a->items[idx]);
a->count -= 1; a->count -= 1;
return 1; return 1;
} }
int array_insert(Value *v, int index, Value newElem) { int array_insert(Value *v, int index, Value newElem) {
if (!v || v->type != VAL_ARRAY || !v->arr) return -1; if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
Array *a = (Array*)v->arr; Array *a = (Array *)v->arr;
if (index < 0) index = 0; if (index < 0) index = 0;
if (index > a->count) index = a->count; if (index > a->count) index = a->count;
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1)); Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
if (!newItems) { free_value(newElem); return -1; } if (!newItems) {
free_value(newElem);
return -1;
}
a->items = newItems; a->items = newItems;
/* shift right */ /* shift right */
for (int i = a->count; i > index; --i) { for (int i = a->count; i > index; --i) {
@ -185,10 +195,12 @@ int array_insert(Value *v, int index, Value newElem) {
int array_remove(Value *v, int index, Value *out) { int array_remove(Value *v, int index, Value *out) {
if (!v || v->type != VAL_ARRAY || !v->arr) return 0; if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
Array *a = (Array*)v->arr; Array *a = (Array *)v->arr;
if (index < 0 || index >= a->count) return 0; if (index < 0 || index >= a->count) return 0;
if (out) *out = a->items[index]; /* transfer ownership */ if (out)
else free_value(a->items[index]); *out = a->items[index]; /* transfer ownership */
else
free_value(a->items[index]);
/* shift left */ /* shift left */
for (int i = index; i < a->count - 1; ++i) { for (int i = index; i < a->count - 1; ++i) {
a->items[i] = a->items[i + 1]; a->items[i] = a->items[i + 1];
@ -199,7 +211,7 @@ int array_remove(Value *v, int index, Value *out) {
Value array_slice(const Value *v, int start, int end) { Value array_slice(const Value *v, int start, int end) {
if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil(); if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil();
const Array *a = (const Array*)v->arr; const Array *a = (const Array *)v->arr;
int n = a->count; int n = a->count;
if (start < 0) start = 0; if (start < 0) start = 0;
if (end < 0 || end > n) end = n; if (end < 0 || end > n) end = n;
@ -213,16 +225,18 @@ Value array_slice(const Value *v, int start, int end) {
Value array_concat(const Value *av, const Value *bv) { Value array_concat(const Value *av, const Value *bv) {
if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil(); if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil();
const Array *a = (const Array*)av->arr; const Array *a = (const Array *)av->arr;
const Array *b = (const Array*)bv->arr; const Array *b = (const Array *)bv->arr;
int na = a ? a->count : 0; int na = a ? a->count : 0;
int nb = b ? b->count : 0; int nb = b ? b->count : 0;
int total = na + nb; int total = na + nb;
if (total <= 0) return make_array_from_values(NULL, 0); if (total <= 0) return make_array_from_values(NULL, 0);
Value *tmp = (Value*)malloc(sizeof(Value) * total); Value *tmp = (Value *)malloc(sizeof(Value) * total);
if (!tmp) return make_nil(); if (!tmp) return make_nil();
for (int i = 0; i < na; ++i) tmp[i] = a->items[i]; for (int i = 0; i < na; ++i)
for (int j = 0; j < nb; ++j) tmp[na + j] = b->items[j]; 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); Value out = make_array_from_values(tmp, total);
/* free temporaries we copied from (deep copy in make_array_from_values) */ /* free temporaries we copied from (deep copy in make_array_from_values) */
free(tmp); free(tmp);
@ -249,14 +263,14 @@ Value copy_value(const Value *v) {
out.fn = v->fn; /* shallow copy pointer */ out.fn = v->fn; /* shallow copy pointer */
break; break;
case VAL_ARRAY: { case VAL_ARRAY: {
Array *a = (Array*)v->arr; Array *a = (Array *)v->arr;
out.arr = (struct Array*)a; out.arr = (struct Array *)a;
if (a) a->refcount++; if (a) a->refcount++;
break; break;
} }
case VAL_MAP: { case VAL_MAP: {
Map *m = (Map*)v->map; Map *m = (Map *)v->map;
out.map = (struct Map*)m; out.map = (struct Map *)m;
if (m) m->refcount++; if (m) m->refcount++;
break; break;
} }
@ -281,12 +295,12 @@ Value deep_copy_value(const Value *v) {
case VAL_FUNCTION: case VAL_FUNCTION:
return make_function(v->fn); /* shallow pointer for function bytecode */ return make_function(v->fn); /* shallow pointer for function bytecode */
case VAL_ARRAY: { case VAL_ARRAY: {
const Array *a = (const Array*)v->arr; const Array *a = (const Array *)v->arr;
if (!a || a->count <= 0) { if (!a || a->count <= 0) {
return make_array_from_values(NULL, 0); return make_array_from_values(NULL, 0);
} }
/* copy items deeply */ /* copy items deeply */
Value *tmp = (Value*)malloc(sizeof(Value) * a->count); Value *tmp = (Value *)malloc(sizeof(Value) * a->count);
if (!tmp) return make_nil(); if (!tmp) return make_nil();
for (int i = 0; i < a->count; ++i) { for (int i = 0; i < a->count; ++i) {
tmp[i] = deep_copy_value(&a->items[i]); tmp[i] = deep_copy_value(&a->items[i]);
@ -299,7 +313,7 @@ Value deep_copy_value(const Value *v) {
return out; return out;
} }
case VAL_MAP: { case VAL_MAP: {
const Map *m = (const Map*)v->map; const Map *m = (const Map *)v->map;
if (!m || m->count <= 0) return make_map_empty(); if (!m || m->count <= 0) return make_map_empty();
Value out = make_map_empty(); Value out = make_map_empty();
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
@ -318,7 +332,7 @@ void free_value(Value v) {
if (v.type == VAL_STRING && v.s) { if (v.type == VAL_STRING && v.s) {
free(v.s); free(v.s);
} else if (v.type == VAL_ARRAY && v.arr) { } else if (v.type == VAL_ARRAY && v.arr) {
Array *a = (Array*)v.arr; Array *a = (Array *)v.arr;
if (--a->refcount == 0) { if (--a->refcount == 0) {
for (int i = 0; i < a->count; ++i) { for (int i = 0; i < a->count; ++i) {
free_value(a->items[i]); free_value(a->items[i]);
@ -327,7 +341,7 @@ void free_value(Value v) {
free(a); free(a);
} }
} else if (v.type == VAL_MAP && v.map) { } else if (v.type == VAL_MAP && v.map) {
Map *m = (Map*)v.map; Map *m = (Map *)v.map;
if (--m->refcount == 0) { if (--m->refcount == 0) {
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
if (m->keys[i]) free(m->keys[i]); if (m->keys[i]) free(m->keys[i]);
@ -356,10 +370,10 @@ void print_value(const Value *v) {
printf("%s", v->i ? "true" : "false"); printf("%s", v->i ? "true" : "false");
break; break;
case VAL_FUNCTION: case VAL_FUNCTION:
printf("<function@%p>", (void*)v->fn); printf("<function@%p>", (void *)v->fn);
break; break;
case VAL_ARRAY: { case VAL_ARRAY: {
const Array *a = (const Array*)v->arr; const Array *a = (const Array *)v->arr;
printf("["); printf("[");
if (a) { if (a) {
for (int i = 0; i < a->count; ++i) { for (int i = 0; i < a->count; ++i) {
@ -371,7 +385,7 @@ void print_value(const Value *v) {
break; break;
} }
case VAL_MAP: { case VAL_MAP: {
const Map *m = (const Map*)v->map; const Map *m = (const Map *)v->map;
printf("{"); printf("{");
if (m) { if (m) {
for (int i = 0; i < m->count; ++i) { for (int i = 0; i < m->count; ++i) {
@ -403,7 +417,7 @@ int value_is_truthy(const Value *v) {
case VAL_FUNCTION: case VAL_FUNCTION:
return 1; return 1;
case VAL_ARRAY: { case VAL_ARRAY: {
const Array *a = (const Array*)v->arr; const Array *a = (const Array *)v->arr;
return a && a->count > 0; return a && a->count > 0;
} }
case VAL_NIL: case VAL_NIL:
@ -432,7 +446,7 @@ char *value_to_string_alloc(const Value *v) {
case VAL_BOOL: case VAL_BOOL:
return strdup(v->i ? "true" : "false"); return strdup(v->i ? "true" : "false");
case VAL_FUNCTION: { case VAL_FUNCTION: {
snprintf(buf, sizeof(buf), "<function@%p>", (void*)v->fn); snprintf(buf, sizeof(buf), "<function@%p>", (void *)v->fn);
return strdup(buf); return strdup(buf);
} }
case VAL_ARRAY: { case VAL_ARRAY: {
@ -444,7 +458,7 @@ char *value_to_string_alloc(const Value *v) {
case VAL_MAP: { case VAL_MAP: {
int n = 0; int n = 0;
if (v->type == VAL_MAP && v->map) { if (v->type == VAL_MAP && v->map) {
const Map *m = (const Map*)v->map; const Map *m = (const Map *)v->map;
n = m ? m->count : 0; n = m ? m->count : 0;
} }
snprintf(buf, sizeof(buf), "{map n=%d}", n); snprintf(buf, sizeof(buf), "{map n=%d}", n);
@ -465,13 +479,16 @@ int value_equals(const Value *a, const Value *b) {
} }
if (a->type != b->type) return 0; if (a->type != b->type) return 0;
switch (a->type) { switch (a->type) {
case VAL_INT: return a->i == b->i; case VAL_INT:
case VAL_BOOL: return (a->i != 0) == (b->i != 0); return a->i == b->i;
case VAL_BOOL:
return (a->i != 0) == (b->i != 0);
case VAL_STRING: { case VAL_STRING: {
const char *sa = a->s ? a->s : ""; const char *sa = a->s ? a->s : "";
const char *sb = b->s ? b->s : ""; const char *sb = b->s ? b->s : "";
return strcmp(sa, sb) == 0; return strcmp(sa, sb) == 0;
} }
default: return 0; default:
return 0;
} }
} }

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file value.h * @file value.h
* @brief Defines the Value type and associated functions for the Fun VM. * @brief Defines the Value type and associated functions for the Fun VM.
* *
* This file defines the `Value` type, which represents all possible data types * This file defines the `Value` type, which represents all possible data types
@ -90,7 +90,7 @@ Value array_concat(const Value *a, const Value *b); /* returns new arra
/* maps (string keys) */ /* maps (string keys) */
Value make_map_empty(void); /* new empty map */ 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_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_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 */ int map_has(const Value *m, const char *key); /* 1/0 */
Value map_keys_array(const Value *m); /* array of strings */ Value map_keys_array(const Value *m); /* array of strings */
Value map_values_array(const Value *m); /* array of values (copies) */ Value map_values_array(const Value *m); /* array of values (copies) */

601
src/vm.c
View file

@ -18,25 +18,25 @@
#endif #endif
#endif #endif
#include <math.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include <stddef.h>
#include <time.h> #include <time.h>
#include <math.h>
#ifdef __unix__ #ifdef __unix__
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> #include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h> #include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
/* For hidden input (password) handling in OP_INPUT_LINE */ /* For hidden input (password) handling in OP_INPUT_LINE */
#include <termios.h> #include <termios.h>
//#include <arpa/inet.h> // #include <arpa/inet.h>
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
@ -52,8 +52,8 @@
/* Shared Notcurses state (optional) */ /* Shared Notcurses state (optional) */
#ifdef FUN_WITH_NOTCURSES #ifdef FUN_WITH_NOTCURSES
# include <wchar.h> /* ensure wcwidth/wcswidth prototypes present before notcurses.h on some systems */ #include <notcurses/notcurses.h>
# include <notcurses/notcurses.h> #include <wchar.h> /* ensure wcwidth/wcswidth prototypes present before notcurses.h on some systems */
#endif #endif
#include "vm/notcurses/common.h" #include "vm/notcurses/common.h"
@ -67,14 +67,14 @@
/* Note: INI opcode handlers are included below; changes in vm/ini/ .c files /* Note: INI opcode handlers are included below; changes in vm/ini/ .c files
* require vm.c to rebuild. */ * require vm.c to rebuild. */
#include "external/json.c" #include "external/json.c"
#include "external/libressl.c"
#include "external/libsql.c" #include "external/libsql.c"
#include "external/pcsc.c" #include "external/openssl.c"
#include "external/pcre2.c" #include "external/pcre2.c"
#include "external/pcsc.c"
#include "external/sqlite.c" #include "external/sqlite.c"
#include "external/tcltk.c" #include "external/tcltk.c"
#include "external/xml2.c" #include "external/xml2.c"
#include "external/openssl.c"
#include "external/libressl.c"
/* forward declarations for include mapping used in error reporting */ /* forward declarations for include mapping used in error reporting */
extern char *preprocess_includes(const char *src); extern char *preprocess_includes(const char *src);
@ -105,21 +105,25 @@ static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) {
/* derive opcode name */ /* derive opcode name */
if (ip >= 0 && ip < f->fn->instr_count) { if (ip >= 0 && ip < f->fn->instr_count) {
int op = f->fn->instructions[ip].op; int op = f->fn->instructions[ip].op;
if (op >= 0 && op < (int)(sizeof(opcode_names)/sizeof(opcode_names[0]))) { if (op >= 0 && op < (int)(sizeof(opcode_names) / sizeof(opcode_names[0]))) {
opname = opcode_names[op]; opname = opcode_names[op];
} }
} }
/* derive source line by scanning back to the most recent OP_LINE marker */ /* derive source line by scanning back to the most recent OP_LINE marker */
for (int i = ip; i >= 0; --i) { for (int i = ip; i >= 0; --i) {
Instruction prev = f->fn->instructions[i]; Instruction prev = f->fn->instructions[i];
if (prev.op == OP_LINE) { line = prev.operand; break; } if (prev.op == OP_LINE) {
line = prev.operand;
break;
}
} }
/* fallback to VM's last recorded line if no marker found */ /* fallback to VM's last recorded line if no marker found */
if (line <= 0) line = g_active_vm->current_line > 0 ? g_active_vm->current_line : 1; if (line <= 0) line = g_active_vm->current_line > 0 ? g_active_vm->current_line : 1;
/* If this function was compiled from an include-expanded source, map to the included file */ /* If this function was compiled from an include-expanded source, map to the included file */
if (sfile && line > 0) { if (sfile && line > 0) {
char mapped_path[1024]; int mapped_line = line; char mapped_path[1024];
int mapped_line = line;
if (map_expanded_line_to_include(sfile, line, mapped_path, sizeof(mapped_path), &mapped_line)) { if (map_expanded_line_to_include(sfile, line, mapped_path, sizeof(mapped_path), &mapped_line)) {
sfile = strdup(mapped_path); /* leak on purpose for simplicity; this is rare */ sfile = strdup(mapped_path); /* leak on purpose for simplicity; this is rare */
line = mapped_line; line = mapped_line;
@ -157,10 +161,16 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
if (!f) return 0; if (!f) return 0;
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
long sz = ftell(f); long sz = ftell(f);
if (sz < 0) { fclose(f); return 0; } if (sz < 0) {
fclose(f);
return 0;
}
rewind(f); rewind(f);
char *buf = (char*)malloc((size_t)sz + 1); char *buf = (char *)malloc((size_t)sz + 1);
if (!buf) { fclose(f); return 0; } if (!buf) {
fclose(f);
return 0;
}
size_t n = fread(buf, 1, (size_t)sz, f); size_t n = fread(buf, 1, (size_t)sz, f);
fclose(f); fclose(f);
buf[n] = '\0'; buf[n] = '\0';
@ -171,12 +181,16 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
/* find start offset of requested 1-based line */ /* find start offset of requested 1-based line */
size_t len = strlen(prep); size_t len = strlen(prep);
size_t pos = 0; int cur = 1; size_t pos = 0;
int cur = 1;
while (pos < len && cur < line) { while (pos < len && cur < line) {
if (prep[pos] == '\n') cur++; if (prep[pos] == '\n') cur++;
pos++; pos++;
} }
if (cur != line) { free(prep); return 0; } if (cur != line) {
free(prep);
return 0;
}
/* scan backward to find last include marker line */ /* scan backward to find last include marker line */
const char *marker = "// __include_begin__: "; const char *marker = "// __include_begin__: ";
@ -185,13 +199,15 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
while (scan > 0) { while (scan > 0) {
/* find start of this line */ /* find start of this line */
size_t ls = scan; size_t ls = scan;
while (ls > 0 && prep[ls - 1] != '\n') ls--; while (ls > 0 && prep[ls - 1] != '\n')
ls--;
/* check marker */ /* check marker */
if (ls + mlen <= len && strncmp(prep + ls, marker, mlen) == 0) { if (ls + mlen <= len && strncmp(prep + ls, marker, mlen) == 0) {
/* extract included path up to EOL or ' as ' */ /* extract included path up to EOL or ' as ' */
size_t p = ls + mlen; size_t p = ls + mlen;
size_t pe = p; size_t pe = p;
while (pe < len && prep[pe] != '\n' && !(prep[pe] == ' ' && pe + 3 < len && strncmp(prep + pe, " as ", 4) == 0)) pe++; while (pe < len && prep[pe] != '\n' && !(prep[pe] == ' ' && pe + 3 < len && strncmp(prep + pe, " as ", 4) == 0))
pe++;
size_t copy = (pe - p) < (out_path_cap - 1) ? (pe - p) : (out_path_cap - 1); size_t copy = (pe - p) < (out_path_cap - 1) ? (pe - p) : (out_path_cap - 1);
memcpy(out_path, prep + p, copy); memcpy(out_path, prep + p, copy);
out_path[copy] = '\0'; out_path[copy] = '\0';
@ -199,9 +215,13 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
int inner = 1; int inner = 1;
size_t q = pe; size_t q = pe;
/* skip to next line start */ /* skip to next line start */
while (q < len && prep[q] != '\n') q++; while (q < len && prep[q] != '\n')
q++;
if (q < len && prep[q] == '\n') q++; if (q < len && prep[q] == '\n') q++;
while (q < pos) { if (prep[q] == '\n') inner++; q++; } while (q < pos) {
if (prep[q] == '\n') inner++;
q++;
}
*out_line = inner; *out_line = inner;
free(prep); free(prep);
return 1; return 1;
@ -308,17 +328,26 @@ Dev tips:
- You can run scripts/run_examples.sh to sanity-check examples quickly. - You can run scripts/run_examples.sh to sanity-check examples quickly.
*/ */
static const char* value_type_name(ValueType t) { static const char *value_type_name(ValueType t) {
switch (t) { switch (t) {
case VAL_FUNCTION: return "function"; case VAL_FUNCTION:
case VAL_INT: return "int"; return "function";
case VAL_FLOAT: return "float"; case VAL_INT:
case VAL_BOOL: return "boolean"; return "int";
case VAL_ARRAY: return "array"; case VAL_FLOAT:
case VAL_MAP: return "map"; return "float";
case VAL_NIL: return "nil"; case VAL_BOOL:
case VAL_STRING: return "string"; return "boolean";
default: return "unknown"; case VAL_ARRAY:
return "array";
case VAL_MAP:
return "map";
case VAL_NIL:
return "nil";
case VAL_STRING:
return "string";
default:
return "unknown";
} }
} }
@ -328,7 +357,8 @@ void vm_clear_output(VM *vm) {
} }
vm->output_count = 0; vm->output_count = 0;
// reset partial flags // reset partial flags
for (int i = 0; i < OUTPUT_SIZE; ++i) vm->output_is_partial[i] = 0; for (int i = 0; i < OUTPUT_SIZE; ++i)
vm->output_is_partial[i] = 0;
} }
void vm_free(VM *vm) { void vm_free(VM *vm) {
@ -391,7 +421,7 @@ void vm_debug_reset(VM *vm) {
int vm_debug_add_breakpoint(VM *vm, const char *file, int line) { int vm_debug_add_breakpoint(VM *vm, const char *file, int line) {
if (!file || line <= 0) return -1; if (!file || line <= 0) return -1;
if (vm->break_count >= (int)(sizeof(vm->breakpoints)/sizeof(vm->breakpoints[0]))) return -1; if (vm->break_count >= (int)(sizeof(vm->breakpoints) / sizeof(vm->breakpoints[0]))) return -1;
int id = vm->break_count++; int id = vm->break_count++;
vm->breakpoints[id].file = strdup(file); vm->breakpoints[id].file = strdup(file);
vm->breakpoints[id].line = line; vm->breakpoints[id].line = line;
@ -476,7 +506,7 @@ int64_t vm_pop_i64(VM *vm) {
if (v.type == VAL_INT) { if (v.type == VAL_INT) {
out = v.i; out = v.i;
} else if (v.type == VAL_FLOAT) { } else if (v.type == VAL_FLOAT) {
out = (int64_t) v.d; out = (int64_t)v.d;
} else { } else {
fprintf(stderr, "Runtime type error: expected int/float on stack, got %s\n", value_type_name(v.type)); fprintf(stderr, "Runtime type error: expected int/float on stack, got %s\n", value_type_name(v.type));
free_value(v); free_value(v);
@ -501,7 +531,7 @@ size_t vm_value_sizeof(void) {
} }
void *vm_as_mut_ptr(VM *vm) { void *vm_as_mut_ptr(VM *vm) {
return (void*)vm; return (void *)vm;
} }
size_t vm_offset_of_exit_code(void) { size_t vm_offset_of_exit_code(void) {
@ -523,7 +553,8 @@ size_t vm_offset_of_globals(void) {
static void frame_init(Frame *f) { static void frame_init(Frame *f) {
f->fn = NULL; f->fn = NULL;
f->ip = 0; f->ip = 0;
for (int i = 0; i < MAX_FRAME_LOCALS; ++i) f->locals[i] = make_nil(); for (int i = 0; i < MAX_FRAME_LOCALS; ++i)
f->locals[i] = make_nil();
f->try_sp = -1; f->try_sp = -1;
} }
@ -531,7 +562,8 @@ void vm_init(VM *vm) {
vm->sp = -1; vm->sp = -1;
vm->fp = -1; vm->fp = -1;
vm->output_count = 0; vm->output_count = 0;
for (int i = 0; i < OUTPUT_SIZE; ++i) vm->output_is_partial[i] = 0; for (int i = 0; i < OUTPUT_SIZE; ++i)
vm->output_is_partial[i] = 0;
vm->instr_count = 0; vm->instr_count = 0;
vm->exit_code = 0; vm->exit_code = 0;
vm->trace_enabled = 0; vm->trace_enabled = 0;
@ -544,7 +576,7 @@ void vm_init(VM *vm) {
vm->debug_step_start_ic = 0; vm->debug_step_start_ic = 0;
vm->debug_stop_requested = 0; vm->debug_stop_requested = 0;
vm->break_count = 0; vm->break_count = 0;
for (int i = 0; i < (int)(sizeof(vm->breakpoints)/sizeof(vm->breakpoints[0])); ++i) { for (int i = 0; i < (int)(sizeof(vm->breakpoints) / sizeof(vm->breakpoints[0])); ++i) {
vm->breakpoints[i].file = NULL; vm->breakpoints[i].file = NULL;
vm->breakpoints[i].line = 0; vm->breakpoints[i].line = 0;
vm->breakpoints[i].active = 0; vm->breakpoints[i].active = 0;
@ -656,13 +688,15 @@ void vm_run(VM *vm, Bytecode *entry) {
vm->instr_count++; /* count each executed instruction */ vm->instr_count++; /* count each executed instruction */
if (vm->trace_enabled) { if (vm->trace_enabled) {
const char *opname = (inst.op >= 0 && inst.op < (int)(sizeof(opcode_names)/sizeof(opcode_names[0]))) const char *opname = (inst.op >= 0 && inst.op < (int)(sizeof(opcode_names) / sizeof(opcode_names[0])))
? opcode_names[inst.op] : "???"; ? opcode_names[inst.op]
: "???";
const char *fname = f->fn && f->fn->name ? f->fn->name : "<entry>"; const char *fname = f->fn && f->fn->name ? f->fn->name : "<entry>";
const char *sfile = f->fn && f->fn->source_file ? f->fn->source_file : "<unknown>"; const char *sfile = f->fn && f->fn->source_file ? f->fn->source_file : "<unknown>";
/* Dump up to top 4 stack values */ /* Dump up to top 4 stack values */
int count = vm->sp + 1; int count = vm->sp + 1;
int start = count - 4; if (start < 0) start = 0; int start = count - 4;
if (start < 0) start = 0;
fprintf(stdout, "TRACE %s:%d %s ip=%d %-14s %d | stack[%d]=[", sfile, vm->current_line, fname, f->ip - 1, opname, inst.operand, count); fprintf(stdout, "TRACE %s:%d %s ip=%d %-14s %d | stack[%d]=[", sfile, vm->current_line, fname, f->ip - 1, opname, inst.operand, count);
for (int i = start; i < count; ++i) { for (int i = start; i < count; ++i) {
char *sv = value_to_string_alloc(&vm->stack[i]); char *sv = value_to_string_alloc(&vm->stack[i]);
@ -692,231 +726,231 @@ void vm_run(VM *vm, Bytecode *entry) {
} }
switch (inst.op) { switch (inst.op) {
/* All opcode handlers as .c includes */ /* All opcode handlers as .c includes */
#include "vm/arithmetic/add.c" #include "vm/arithmetic/add.c"
#include "vm/arithmetic/div.c" #include "vm/arithmetic/div.c"
#include "vm/arithmetic/mul.c" #include "vm/arithmetic/mul.c"
#include "vm/arithmetic/sub.c" #include "vm/arithmetic/sub.c"
#include "vm/arrays/apop.c" #include "vm/arrays/apop.c"
#include "vm/arrays/clear.c" #include "vm/arrays/clear.c"
#include "vm/arrays/contains.c" #include "vm/arrays/contains.c"
#include "vm/arrays/enumerate.c" #include "vm/arrays/enumerate.c"
#include "vm/arrays/index_get.c" #include "vm/arrays/index_get.c"
#include "vm/arrays/index_of.c" #include "vm/arrays/index_of.c"
#include "vm/arrays/index_set.c" #include "vm/arrays/index_set.c"
#include "vm/arrays/insert.c" #include "vm/arrays/insert.c"
#include "vm/arrays/join.c" #include "vm/arrays/join.c"
#include "vm/arrays/make_array.c" #include "vm/arrays/make_array.c"
#include "vm/arrays/push.c" #include "vm/arrays/push.c"
#include "vm/arrays/remove.c" #include "vm/arrays/remove.c"
#include "vm/arrays/set.c" #include "vm/arrays/set.c"
#include "vm/arrays/slice.c" #include "vm/arrays/slice.c"
#include "vm/arrays/zip.c" #include "vm/arrays/zip.c"
/* Bitwise and shifts/rotates */ /* Bitwise and shifts/rotates */
#include "vm/bitwise/band.c" #include "vm/bitwise/band.c"
#include "vm/bitwise/bor.c" #include "vm/bitwise/bnot.c"
#include "vm/bitwise/bxor.c" #include "vm/bitwise/bor.c"
#include "vm/bitwise/bnot.c" #include "vm/bitwise/bxor.c"
#include "vm/bitwise/shl.c" #include "vm/bitwise/rol.c"
#include "vm/bitwise/shr.c" #include "vm/bitwise/ror.c"
#include "vm/bitwise/rol.c" #include "vm/bitwise/shl.c"
#include "vm/bitwise/ror.c" #include "vm/bitwise/shr.c"
#include "vm/core/call.c" #include "vm/core/call.c"
#include "vm/core/dup.c" #include "vm/core/dup.c"
#include "vm/core/exit.c" #include "vm/core/exit.c"
#include "vm/core/halt.c" #include "vm/core/halt.c"
#include "vm/core/jump.c" #include "vm/core/jump.c"
#include "vm/core/jump_if_false.c" #include "vm/core/jump_if_false.c"
#include "vm/core/load_const.c" #include "vm/core/load_const.c"
#include "vm/core/load_global.c" #include "vm/core/load_global.c"
#include "vm/core/load_local.c" #include "vm/core/load_local.c"
#include "vm/core/nop.c" #include "vm/core/nop.c"
#include "vm/core/pop.c" #include "vm/core/pop.c"
#include "vm/core/return.c" #include "vm/core/return.c"
#include "vm/core/store_global.c" #include "vm/core/store_global.c"
#include "vm/core/store_local.c" #include "vm/core/store_local.c"
#include "vm/core/swap.c" #include "vm/core/swap.c"
#include "vm/core/throw.c" #include "vm/core/throw.c"
#include "vm/core/try_pop.c" #include "vm/core/try_pop.c"
#include "vm/core/try_push.c" #include "vm/core/try_push.c"
#include "vm/io/read_file.c" #include "vm/io/input_line.c"
#include "vm/io/write_file.c" #include "vm/io/read_file.c"
#include "vm/io/input_line.c" #include "vm/io/write_file.c"
#include "vm/logic/and.c" #include "vm/logic/and.c"
#include "vm/logic/eq.c" #include "vm/logic/eq.c"
#include "vm/logic/gt.c" #include "vm/logic/gt.c"
#include "vm/logic/gte.c" #include "vm/logic/gte.c"
#include "vm/logic/lt.c" #include "vm/logic/lt.c"
#include "vm/logic/lte.c" #include "vm/logic/lte.c"
#include "vm/logic/neq.c" #include "vm/logic/neq.c"
#include "vm/logic/not.c" #include "vm/logic/not.c"
#include "vm/logic/or.c" #include "vm/logic/or.c"
#include "vm/maps/has_key.c" #include "vm/maps/has_key.c"
#include "vm/maps/keys.c" #include "vm/maps/keys.c"
#include "vm/maps/make_map.c" #include "vm/maps/make_map.c"
#include "vm/maps/values.c" #include "vm/maps/values.c"
#include "vm/math/abs.c" #include "vm/math/abs.c"
#include "vm/math/clamp.c" #include "vm/math/ceil.c"
#include "vm/math/max.c" #include "vm/math/clamp.c"
#include "vm/math/min.c" #include "vm/math/cos.c"
#include "vm/math/fmax.c" #include "vm/math/exp.c"
#include "vm/math/fmin.c" #include "vm/math/floor.c"
#include "vm/math/mod.c" #include "vm/math/fmax.c"
#include "vm/math/pow.c" #include "vm/math/fmin.c"
#include "vm/math/floor.c" #include "vm/math/gcd.c"
#include "vm/math/ceil.c" #include "vm/math/isqrt.c"
#include "vm/math/trunc.c" #include "vm/math/lcm.c"
#include "vm/math/round.c" #include "vm/math/log.c"
#include "vm/math/sin.c" #include "vm/math/log10.c"
#include "vm/math/cos.c" #include "vm/math/max.c"
#include "vm/math/tan.c" #include "vm/math/min.c"
#include "vm/math/exp.c" #include "vm/math/mod.c"
#include "vm/math/log.c" #include "vm/math/pow.c"
#include "vm/math/log10.c" #include "vm/math/random_int.c"
#include "vm/math/sqrt.c" #include "vm/math/random_seed.c"
#include "vm/math/random_int.c" #include "vm/math/round.c"
#include "vm/math/random_seed.c" #include "vm/math/sign.c"
#include "vm/math/gcd.c" #include "vm/math/sin.c"
#include "vm/math/lcm.c" #include "vm/math/sqrt.c"
#include "vm/math/isqrt.c" #include "vm/math/tan.c"
#include "vm/math/sign.c" #include "vm/math/trunc.c"
/* Rust FFI demo opcode(s) */ /* Rust FFI demo opcode(s) */
#include "vm/rust/hello.c" #include "vm/rust/get_sp.c"
#include "vm/rust/hello_args.c" #include "vm/rust/hello.c"
#include "vm/rust/hello_args_return.c" #include "vm/rust/hello_args.c"
#include "vm/rust/get_sp.c" #include "vm/rust/hello_args_return.c"
#include "vm/rust/set_exit.c" #include "vm/rust/set_exit.c"
#include "vm/os/env.c" #include "vm/os/clock_mono_ms.c"
#include "vm/os/env_all.c" #include "vm/os/date_format.c"
#include "vm/os/fun_version.c" #include "vm/os/env.c"
#include "vm/os/sleep_ms.c" #include "vm/os/env_all.c"
#include "vm/os/thread_join.c" #include "vm/os/fun_version.c"
#include "vm/os/thread_spawn.c" #include "vm/os/proc_run.c"
#include "vm/os/proc_run.c" #include "vm/os/proc_system.c"
#include "vm/os/proc_system.c" #include "vm/os/random_number.c"
#include "vm/os/time_now_ms.c" #include "vm/os/serial_close.c"
#include "vm/os/clock_mono_ms.c" #include "vm/os/serial_config.c"
#include "vm/os/date_format.c" #include "vm/os/serial_open.c"
#include "vm/os/random_number.c" #include "vm/os/serial_recv.c"
#include "vm/os/serial_open.c" #include "vm/os/serial_send.c"
#include "vm/os/serial_config.c" #include "vm/os/sleep_ms.c"
#include "vm/os/serial_send.c" #include "vm/os/thread_join.c"
#include "vm/os/serial_recv.c" #include "vm/os/thread_spawn.c"
#include "vm/os/serial_close.c" #include "vm/os/time_now_ms.c"
/* Socket ops */ /* Socket ops */
#include "vm/os/socket_tcp_listen.c" #include "vm/os/socket_close.c"
#include "vm/os/socket_tcp_accept.c" #include "vm/os/socket_recv.c"
#include "vm/os/socket_tcp_connect.c" #include "vm/os/socket_send.c"
#include "vm/os/socket_send.c" #include "vm/os/socket_tcp_accept.c"
#include "vm/os/socket_recv.c" #include "vm/os/socket_tcp_connect.c"
#include "vm/os/socket_close.c" #include "vm/os/socket_tcp_listen.c"
#include "vm/os/socket_unix_listen.c" #include "vm/os/socket_unix_connect.c"
#include "vm/os/socket_unix_connect.c" #include "vm/os/socket_unix_listen.c"
#ifdef FUN_WITH_PCSC #ifdef FUN_WITH_PCSC
#include "vm/pcsc/establish.c" #include "vm/pcsc/connect.c"
#include "vm/pcsc/release.c" #include "vm/pcsc/disconnect.c"
#include "vm/pcsc/list_readers.c" #include "vm/pcsc/establish.c"
#include "vm/pcsc/connect.c" #include "vm/pcsc/list_readers.c"
#include "vm/pcsc/disconnect.c" #include "vm/pcsc/release.c"
#include "vm/pcsc/transmit.c" #include "vm/pcsc/transmit.c"
#endif #endif
/* JSON ops (implemented in jsonc.c, included above) */ /* JSON ops (implemented in jsonc.c, included above) */
#ifdef FUN_WITH_JSON #ifdef FUN_WITH_JSON
#include "vm/json/parse.c" #include "vm/json/from_file.c"
#include "vm/json/stringify.c" #include "vm/json/parse.c"
#include "vm/json/from_file.c" #include "vm/json/stringify.c"
#include "vm/json/to_file.c" #include "vm/json/to_file.c"
#endif #endif
/* XML ops (libxml2) */ /* XML ops (libxml2) */
#ifdef FUN_WITH_XML2 #ifdef FUN_WITH_XML2
#include "vm/xml/parse.c" #include "vm/xml/name.c"
#include "vm/xml/root.c" #include "vm/xml/parse.c"
#include "vm/xml/name.c" #include "vm/xml/root.c"
#include "vm/xml/text.c" #include "vm/xml/text.c"
#endif #endif
/* INI ops (iniparser 4.2.6) */ /* INI ops (iniparser 4.2.6) */
#ifdef FUN_WITH_INI #ifdef FUN_WITH_INI
#include "vm/ini/load.c" #include "vm/ini/free.c"
#include "vm/ini/free.c" #include "vm/ini/get_bool.c"
#include "vm/ini/get_string.c" #include "vm/ini/get_double.c"
#include "vm/ini/get_int.c" #include "vm/ini/get_int.c"
#include "vm/ini/get_double.c" #include "vm/ini/get_string.c"
#include "vm/ini/get_bool.c" #include "vm/ini/load.c"
#include "vm/ini/set.c" #include "vm/ini/save.c"
#include "vm/ini/unset.c" #include "vm/ini/set.c"
#include "vm/ini/save.c" #include "vm/ini/unset.c"
#else #else
#include "vm/ini/stubs.c" #include "vm/ini/stubs.c"
#endif #endif
/* CURL ops */ /* CURL ops */
#ifdef FUN_WITH_CURL #ifdef FUN_WITH_CURL
#include "vm/curl/get.c" #include "vm/curl/download.c"
#include "vm/curl/post.c" #include "vm/curl/get.c"
#include "vm/curl/download.c" #include "vm/curl/post.c"
#endif #endif
/* OpenSSL ops (md5/sha256/sha512/ripemd160) */ /* OpenSSL ops (md5/sha256/sha512/ripemd160) */
#ifdef FUN_WITH_OPENSSL #ifdef FUN_WITH_OPENSSL
#include "vm/openssl/md5.c" #include "vm/openssl/md5.c"
#include "vm/openssl/sha256.c" #include "vm/openssl/ripemd160.c"
#include "vm/openssl/sha512.c" #include "vm/openssl/sha256.c"
#include "vm/openssl/ripemd160.c" #include "vm/openssl/sha512.c"
#endif #endif
/* LibreSSL ops (md5/sha256/sha512/ripemd160) */ /* LibreSSL ops (md5/sha256/sha512/ripemd160) */
#ifdef FUN_WITH_LIBRESSL #ifdef FUN_WITH_LIBRESSL
#include "vm/libressl/md5.c" #include "vm/libressl/md5.c"
#include "vm/libressl/sha256.c" #include "vm/libressl/ripemd160.c"
#include "vm/libressl/sha512.c" #include "vm/libressl/sha256.c"
#include "vm/libressl/ripemd160.c" #include "vm/libressl/sha512.c"
#endif #endif
/* Tk (Tcl/Tk) ops */ /* Tk (Tcl/Tk) ops */
#ifdef FUN_WITH_TCLTK #ifdef FUN_WITH_TCLTK
#include "vm/tk/eval.c" #include "vm/tk/bind.c"
#include "vm/tk/result.c" #include "vm/tk/button.c"
#include "vm/tk/loop.c" #include "vm/tk/eval.c"
#include "vm/tk/wm_title.c" #include "vm/tk/label.c"
#include "vm/tk/label.c" #include "vm/tk/loop.c"
#include "vm/tk/button.c" #include "vm/tk/pack.c"
#include "vm/tk/pack.c" #include "vm/tk/result.c"
#include "vm/tk/bind.c" #include "vm/tk/wm_title.c"
#endif #endif
/* Notcurses TUI ops (optional) */ /* Notcurses TUI ops (optional) */
#ifdef FUN_WITH_NOTCURSES #ifdef FUN_WITH_NOTCURSES
#include "vm/notcurses/init.c" #include "vm/notcurses/clear.c"
#include "vm/notcurses/shutdown.c" #include "vm/notcurses/draw_text.c"
#include "vm/notcurses/clear.c" #include "vm/notcurses/getch.c"
#include "vm/notcurses/draw_text.c" #include "vm/notcurses/init.c"
#include "vm/notcurses/getch.c" #include "vm/notcurses/shutdown.c"
#endif #endif
/* SQLite ops */ /* SQLite ops */
#ifdef FUN_WITH_SQLITE #ifdef FUN_WITH_SQLITE
#include "vm/sqlite/open.c" #include "vm/sqlite/close.c"
#include "vm/sqlite/close.c" #include "vm/sqlite/exec.c"
#include "vm/sqlite/exec.c" #include "vm/sqlite/open.c"
#include "vm/sqlite/query.c" #include "vm/sqlite/query.c"
#endif #endif
/* C++ demo opcodes (guarded) */ /* C++ demo opcodes (guarded) */
#if defined(FUN_WITH_CPP) #if defined(FUN_WITH_CPP)
case OP_CPP_ADD: { case OP_CPP_ADD: {
int rc = fun_op_cpp_add(vm); int rc = fun_op_cpp_add(vm);
if (rc != 0) { if (rc != 0) {
@ -924,53 +958,54 @@ void vm_run(VM *vm, Bytecode *entry) {
} }
break; break;
} }
#else #else
case OP_CPP_ADD: { case OP_CPP_ADD: {
vm_raise_error(vm, "CPP support is not enabled (build with -DFUN_WITH_CPP=ON)"); vm_raise_error(vm, "CPP support is not enabled (build with -DFUN_WITH_CPP=ON)");
break; break;
} }
#endif #endif
/* libsql ops (independent) */ /* libsql ops (independent) */
#ifdef FUN_WITH_LIBSQL #ifdef FUN_WITH_LIBSQL
#include "vm/libsql/open.c" #include "vm/libsql/close.c"
#include "vm/libsql/close.c" #include "vm/libsql/exec.c"
#include "vm/libsql/exec.c" #include "vm/libsql/open.c"
#include "vm/libsql/query.c" #include "vm/libsql/query.c"
#endif #endif
/* PCRE2 ops */ /* PCRE2 ops */
#ifdef FUN_WITH_PCRE2 #ifdef FUN_WITH_PCRE2
#include "vm/pcre2/test.c" #include "vm/pcre2/findall.c"
#include "vm/pcre2/match.c" #include "vm/pcre2/match.c"
#include "vm/pcre2/findall.c" #include "vm/pcre2/test.c"
#endif #endif
#include "vm/strings/find.c" #include "vm/strings/find.c"
#include "vm/strings/regex_match.c" #include "vm/strings/regex_match.c"
#include "vm/strings/regex_search.c" #include "vm/strings/regex_replace.c"
#include "vm/strings/regex_replace.c" #include "vm/strings/regex_search.c"
#include "vm/strings/split.c" #include "vm/strings/split.c"
#include "vm/strings/substr.c" #include "vm/strings/substr.c"
#include "vm/len.c" #include "vm/cast.c"
#include "vm/line.c" #include "vm/echo.c"
#include "vm/print.c" #include "vm/len.c"
#include "vm/echo.c" #include "vm/line.c"
#include "vm/to_number.c" #include "vm/os/list_dir.c"
#include "vm/to_string.c" #include "vm/print.c"
#include "vm/cast.c" #include "vm/sclamp.c"
#include "vm/typeof.c" #include "vm/to_number.c"
#include "vm/uclamp.c" #include "vm/to_string.c"
#include "vm/sclamp.c" #include "vm/typeof.c"
#include "vm/os/list_dir.c" #include "vm/uclamp.c"
default: default:
if (!opcode_is_valid(inst.op)) { if (!opcode_is_valid(inst.op)) {
fprintf(stderr, "Runtime error: unknown opcode %d (%s) at instruction %d\n", fprintf(stderr, "Runtime error: unknown opcode %d (%s) at instruction %d\n",
inst.op, inst.op,
(inst.op >= 0 && inst.op < sizeof(opcode_names)/sizeof(opcode_names[0])) (inst.op >= 0 && inst.op < sizeof(opcode_names) / sizeof(opcode_names[0]))
? opcode_names[inst.op] : "???", ? opcode_names[inst.op]
: "???",
f->ip - 1); f->ip - 1);
exit(1); exit(1);
} }

View file

@ -20,49 +20,48 @@
#define STACK_SIZE 1024 #define STACK_SIZE 1024
static const char *opcode_names[] = { static const char *opcode_names[] = {
"NOP","LOAD_CONST","LOAD_LOCAL","STORE_LOCAL", "NOP", "LOAD_CONST", "LOAD_LOCAL", "STORE_LOCAL",
"LOAD_GLOBAL","STORE_GLOBAL","ADD","SUB","MUL","DIV", "LOAD_GLOBAL", "STORE_GLOBAL", "ADD", "SUB", "MUL", "DIV",
"LT","LTE","GT","GTE","EQ","NEQ","POP","JUMP", "LT", "LTE", "GT", "GTE", "EQ", "NEQ", "POP", "JUMP",
"JUMP_IF_FALSE","CALL","RETURN","PRINT","ECHO","HALT", "JUMP_IF_FALSE", "CALL", "RETURN", "PRINT", "ECHO", "HALT",
"LINE", "LINE",
"MOD","AND","OR","NOT","DUP","SWAP", "MOD", "AND", "OR", "NOT", "DUP", "SWAP",
"MAKE_ARRAY","INDEX_GET","INDEX_SET", "MAKE_ARRAY", "INDEX_GET", "INDEX_SET",
"LEN","PUSH","APOP","SET","INSERT","REMOVE","SLICE", "LEN", "PUSH", "APOP", "SET", "INSERT", "REMOVE", "SLICE",
"TO_NUMBER","TO_STRING","CAST","TYPEOF", "TO_NUMBER", "TO_STRING", "CAST", "TYPEOF",
"SPLIT","JOIN","SUBSTR","FIND", "SPLIT", "JOIN", "SUBSTR", "FIND",
"REGEX_MATCH","REGEX_SEARCH","REGEX_REPLACE", "REGEX_MATCH", "REGEX_SEARCH", "REGEX_REPLACE",
"CONTAINS","INDEX_OF","CLEAR", "CONTAINS", "INDEX_OF", "CLEAR",
"ENUMERATE","ZIP", "ENUMERATE", "ZIP",
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT", "MIN", "MAX", "CLAMP", "ABS", "POW", "RANDOM_SEED", "RANDOM_INT",
"MAKE_MAP","KEYS","VALUES","HAS_KEY", "MAKE_MAP", "KEYS", "VALUES", "HAS_KEY",
"READ_FILE","WRITE_FILE","ENV","INPUT_LINE","PROC_RUN","PROC_SYSTEM", "READ_FILE", "WRITE_FILE", "ENV", "INPUT_LINE", "PROC_RUN", "PROC_SYSTEM",
"TIME_NOW_MS","CLOCK_MONO_MS","DATE_FORMAT", "TIME_NOW_MS", "CLOCK_MONO_MS", "DATE_FORMAT",
"THREAD_SPAWN","THREAD_JOIN","SLEEP_MS", "THREAD_SPAWN", "THREAD_JOIN", "SLEEP_MS",
"RANDOM_NUMBER", "RANDOM_NUMBER",
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR", "BAND", "BOR", "BXOR", "BNOT", "SHL", "SHR", "ROTL", "ROTR",
"JSON_PARSE","JSON_STRINGIFY","JSON_FROM_FILE","JSON_TO_FILE", "JSON_PARSE", "JSON_STRINGIFY", "JSON_FROM_FILE", "JSON_TO_FILE",
"CURL_GET","CURL_POST","CURL_DOWNLOAD", "CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
"SQLITE_OPEN","SQLITE_CLOSE","SQLITE_EXEC","SQLITE_QUERY", "SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
"LIBSQL_OPEN","LIBSQL_CLOSE","LIBSQL_EXEC","LIBSQL_QUERY", "LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
"PCSC_ESTABLISH","PCSC_RELEASE","PCSC_LIST_READERS","PCSC_CONNECT","PCSC_DISCONNECT","PCSC_TRANSMIT", "PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
"PCRE2_TEST","PCRE2_MATCH","PCRE2_FINDALL", "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", "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", "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", "SOCK_TCP_LISTEN", "SOCK_TCP_ACCEPT", "SOCK_TCP_CONNECT", "SOCK_SEND", "SOCK_RECV", "SOCK_CLOSE", "SOCK_UNIX_LISTEN", "SOCK_UNIX_CONNECT",
"EXIT", "EXIT",
"OS_LIST_DIR", "OS_LIST_DIR",
"TK_BIND", "TK_BIND",
"SERIAL_OPEN","SERIAL_CONFIG","SERIAL_SEND","SERIAL_RECV","SERIAL_CLOSE", "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", "TK_EVAL", "TK_RESULT", "TK_LOOP", "TK_WM_TITLE", "TK_LABEL", "TK_BUTTON", "TK_PACK",
"TRY_PUSH","TRY_POP","THROW", "TRY_PUSH", "TRY_POP", "THROW",
"FMIN","FMAX", "FMIN", "FMAX",
/* Rust FFI demo */ /* Rust FFI demo */
"RUST_HELLO","RUST_HELLO_ARGS","RUST_HELLO_ARGS_RETURN","RUST_GET_SP","RUST_SET_EXIT", "RUST_HELLO", "RUST_HELLO_ARGS", "RUST_HELLO_ARGS_RETURN", "RUST_GET_SP", "RUST_SET_EXIT",
/* C++ demo */ /* C++ demo */
"CPP_ADD", "CPP_ADD",
/* Notcurses TUI (optional) */ /* Notcurses TUI (optional) */
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH" "NC_INIT", "NC_SHUTDOWN", "NC_CLEAR", "NC_DRAW_TEXT", "NC_GETCH"};
};
typedef struct { typedef struct {
Bytecode *fn; Bytecode *fn;

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file add.c * @file add.c
* @brief Implements the OP_ADD opcode for arithmetic and string concatenation in the VM. * @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 * This file handles the OP_ADD instruction, which performs addition or concatenation
@ -57,7 +57,7 @@ case OP_ADD: {
const char *sb = b.s ? b.s : ""; const char *sb = b.s ? b.s : "";
size_t la = strlen(sa); size_t la = strlen(sa);
size_t lb = strlen(sb); size_t lb = strlen(sb);
char *buf = (char*)malloc(la + lb + 1); char *buf = (char *)malloc(la + lb + 1);
if (!buf) { if (!buf) {
fprintf(stderr, "Runtime error: out of memory during string concatenation\n"); fprintf(stderr, "Runtime error: out of memory during string concatenation\n");
exit(1); exit(1);

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file div.c * @file div.c
* @brief Implements the OP_DIV opcode for integer division in the VM. * @brief Implements the OP_DIV opcode for integer division in the VM.
* *
* This file handles the OP_DIV instruction, which performs integer division * This file handles the OP_DIV instruction, which performs integer division

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file mul.c * @file mul.c
* @brief Implements the OP_MUL opcode for integer multiplication in the VM. * @brief Implements the OP_MUL opcode for integer multiplication in the VM.
* *
* This file handles the OP_MUL instruction, which performs integer multiplication * This file handles the OP_MUL instruction, which performs integer multiplication

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file sub.c * @file sub.c
* @brief Implements the OP_SUB opcode for integer subtraction in the VM. * @brief Implements the OP_SUB opcode for integer subtraction in the VM.
* *
* This file handles the OP_SUB instruction, which performs integer subtraction * This file handles the OP_SUB instruction, which performs integer subtraction

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file apop.c * @file apop.c
* @brief Implements the OP_APOP opcode for removing elements from arrays in the VM. * @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 * This file handles the OP_APOP instruction, which removes the last element from an array

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file contains.c * @file contains.c
* @brief Implements the OP_CONTAINS opcode for checking array membership in the VM. * @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. * This file handles the OP_CONTAINS instruction, which checks if a value is present in an array.

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file enumerate.c * @file enumerate.c
* @brief Implements the OP_ENUMERATE opcode for enumerating arrays in the VM. * @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 * This file handles the OP_ENUMERATE instruction, which creates an array of [index, value] pairs

View file

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

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file index_of.c * @file index_of.c
* @brief Implements the OP_INDEX_OF opcode for finding the index of a value in an array in the VM. * @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. * This file handles the OP_INDEX_OF instruction, which finds the index of a value in an array.

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file index_set.c * @file index_set.c
* @brief Implements the OP_INDEX_SET opcode for array and map assignment in the VM. * @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 * This file handles the OP_INDEX_SET instruction, which assigns a value to an
@ -32,7 +32,6 @@
* @date 2025-10-16 * @date 2025-10-16
*/ */
case OP_INDEX_SET: { case OP_INDEX_SET: {
Value v = pop_value(vm); Value v = pop_value(vm);
Value idx = pop_value(vm); Value idx = pop_value(vm);
@ -42,16 +41,24 @@ case OP_INDEX_SET: {
container.type, idx.type, v.type); container.type, idx.type, v.type);
#endif #endif
if (container.type == VAL_ARRAY) { if (container.type == VAL_ARRAY) {
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_SET index must be int for array\n"); exit(1); } 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)) { if (!array_set(&container, (int)idx.i, v)) {
fprintf(stderr, "Runtime error: index out of range\n"); exit(1); fprintf(stderr, "Runtime error: index out of range\n");
exit(1);
} }
free_value(container); free_value(container);
free_value(idx); free_value(idx);
} else if (container.type == VAL_MAP) { } 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 (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)) { if (!map_set(&container, idx.s ? idx.s : "", v)) {
fprintf(stderr, "Runtime error: map set failed\n"); exit(1); fprintf(stderr, "Runtime error: map set failed\n");
exit(1);
} }
free_value(container); free_value(container);
free_value(idx); free_value(idx);

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file insert.c * @file insert.c
* @brief Implements the OP_INSERT opcode for inserting elements into arrays in the VM. * @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 * This file handles the OP_INSERT instruction, which inserts a value into an array

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file join.c * @file join.c
* @brief Implements the OP_JOIN opcode for joining array elements into a string in the VM. * @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 * This file handles the OP_JOIN instruction, which joins the elements of an array into a string

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file make_array.c * @file make_array.c
* @brief Implements the OP_MAKE_ARRAY opcode for creating arrays in the VM. * @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, * This file handles the OP_MAKE_ARRAY instruction, which pops `n` values from the stack,
@ -39,8 +39,11 @@ case OP_MAKE_ARRAY: {
exit(1); exit(1);
} }
/* pop n values into temp array preserving original order */ /* pop n values into temp array preserving original order */
Value *vals = (Value*)malloc(sizeof(Value) * n); Value *vals = (Value *)malloc(sizeof(Value) * n);
if (!vals) { fprintf(stderr, "Runtime error: OOM in MAKE_ARRAY\n"); exit(1); } if (!vals) {
fprintf(stderr, "Runtime error: OOM in MAKE_ARRAY\n");
exit(1);
}
for (int i = n - 1; i >= 0; --i) { for (int i = n - 1; i >= 0; --i) {
vals[i] = pop_value(vm); /* take ownership */ vals[i] = pop_value(vm); /* take ownership */
} }

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file arr_push.c * @file arr_push.c
* @brief Implements the OP_ARR_PUSH opcode for appending elements to arrays in the VM. * @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. * This file handles the OP_ARR_PUSH instruction, which appends a value to the end of an array.

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file arr_remove.c * @file arr_remove.c
* @brief Implements the OP_ARR_REMOVE opcode for removing elements from arrays in the VM. * @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 * This file handles the OP_ARR_REMOVE instruction, which removes an element from an array

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file arr_set.c * @file arr_set.c
* @brief Implements the OP_ARR_SET opcode for setting elements in arrays in the VM. * @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. * This file handles the OP_ARR_SET instruction, which sets a value at a specified index in an array.

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file slice.c * @file slice.c
* @brief Implements the OP_SLICE opcode for array slicing in the VM. * @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 * This file handles the OP_SLICE instruction, which creates a new array containing

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file zip.c * @file zip.c
* @brief Implements the OP_ZIP opcode for array zipping in the VM. * @brief Implements the OP_ZIP opcode for array zipping in the VM.
* *
* This file handles the OP_ZIP instruction, which combines two arrays into * This file handles the OP_ZIP instruction, which combines two arrays into

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -45,12 +45,16 @@ case OP_CAST: {
} else if (v.type == VAL_STRING) { } else if (v.type == VAL_STRING) {
const char *s = v.s ? v.s : ""; const char *s = v.s ? v.s : "";
const char *p = s; const char *p = s;
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++; while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
p++;
char *endp = NULL; char *endp = NULL;
long long parsed = strtoll(p, &endp, 10); long long parsed = strtoll(p, &endp, 10);
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n')) endp++; while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n'))
if (endp && *endp != '\0') out = make_int(0); endp++;
else out = make_int((int64_t)parsed); if (endp && *endp != '\0')
out = make_int(0);
else
out = make_int((int64_t)parsed);
} else { } else {
out = make_int(0); out = make_int(0);
} }

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file call.c * @file call.c
* @brief Implements the OP_CALL opcode for function calls in the VM. * @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. * This file handles the OP_CALL instruction, which calls a function with arguments.
@ -33,7 +33,7 @@ case OP_CALL: {
/* collect args in reverse (preserve order) */ /* collect args in reverse (preserve order) */
Value *args = NULL; Value *args = NULL;
if (argc > 0) { if (argc > 0) {
args = (Value*)malloc(sizeof(Value) * argc); args = (Value *)malloc(sizeof(Value) * argc);
/* pop args into array in reverse */ /* pop args into array in reverse */
for (int i = argc - 1; i >= 0; --i) { for (int i = argc - 1; i >= 0; --i) {
args[i] = pop_value(vm); args[i] = pop_value(vm);

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file dup.c * @file dup.c
* @brief Implements the OP_DUP opcode for duplicating the top stack value in the VM. * @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. * This file handles the OP_DUP instruction, which duplicates the top value on the stack.
@ -25,7 +25,7 @@
* // Bytecode: OP_DUP * // Bytecode: OP_DUP
* // Stack before: [42] * // Stack before: [42]
* // Stack after: [42, 42] * // Stack after: [42, 42]
* *
* @author Johannes Findeisen * @author Johannes Findeisen
* @date 2025-10-16 * @date 2025-10-16
*/ */

View file

@ -10,7 +10,7 @@
*/ */
/** /**
* @file exit.c * @file exit.c
* @brief Implements the OP_EXIT opcode to terminate the script with an exit code. * @brief Implements the OP_EXIT opcode to terminate the script with an exit code.
* *
* Behavior: * Behavior:

View file

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

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file jump.c * @file jump.c
* @brief Implements the OP_JUMP opcode for unconditional jumps in the VM. * @brief Implements the OP_JUMP opcode for unconditional jumps in the VM.
* *
* This file handles the OP_JUMP instruction, which performs an unconditional * This file handles the OP_JUMP instruction, which performs an unconditional

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file jump_if_false.c * @file jump_if_false.c
* @brief Implements the OP_JUMP_IF_FALSE opcode for conditional jumps in the VM. * @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 * This file handles the OP_JUMP_IF_FALSE instruction, which jumps if the top

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file load_const.c * @file load_const.c
* @brief Implements the OP_LOAD_CONST opcode for loading constants in the VM. * @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 * This file handles the OP_LOAD_CONST instruction, which loads a constant value

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file load_global.c * @file load_global.c
* @brief Implements the OP_LOAD_GLOBAL opcode for loading global variables in the VM. * @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 * This file handles the OP_LOAD_GLOBAL instruction, which loads a global variable

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file load_local.c * @file load_local.c
* @brief Implements the OP_LOAD_LOCAL opcode for loading local variables in the VM. * @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 * This file handles the OP_LOAD_LOCAL instruction, which loads a local variable

View file

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

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file pop.c * @file pop.c
* @brief Implements the OP_POP opcode for removing the top stack value in the VM. * @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. * This file handles the OP_POP instruction, which removes the top value from the stack.

View file

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

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file store_global.c * @file store_global.c
* @brief Implements the OP_STORE_GLOBAL opcode for storing global variables in the VM. * @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 * This file handles the OP_STORE_GLOBAL instruction, which stores a value into a global variable

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file store_local.c * @file store_local.c
* @brief Implements the OP_STORE_LOCAL opcode for storing local variables in the VM. * @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 * This file handles the OP_STORE_LOCAL instruction, which stores a value into a local variable

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file swap.c * @file swap.c
* @brief Implements the OP_SWAP opcode for stack manipulation in the VM. * @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 * This file handles the OP_SWAP instruction, which swaps the top two values

View file

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

View file

@ -1,5 +1,5 @@
/* /*
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2026 Johannes Findeisen <you@hanez.org> * Copyright 2026 Johannes Findeisen <you@hanez.org>

View file

@ -17,14 +17,16 @@ case OP_CURL_DOWNLOAD: {
} }
FILE *fp = fopen(path, "wb"); FILE *fp = fopen(path, "wb");
if (!fp) { if (!fp) {
free(url); free(path); free(url);
free(path);
push_value(vm, make_int(0)); push_value(vm, make_int(0));
break; break;
} }
CURL *h = curl_easy_init(); CURL *h = curl_easy_init();
if (!h) { if (!h) {
fclose(fp); fclose(fp);
free(url); free(path); free(url);
free(path);
push_value(vm, make_int(0)); push_value(vm, make_int(0));
break; break;
} }
@ -35,12 +37,18 @@ case OP_CURL_DOWNLOAD: {
CURLcode rc = curl_easy_perform(h); CURLcode rc = curl_easy_perform(h);
curl_easy_cleanup(h); curl_easy_cleanup(h);
fclose(fp); fclose(fp);
free(url); free(path); free(url);
if (rc != CURLE_OK) { push_value(vm, make_int(0)); break; } free(path);
if (rc != CURLE_OK) {
push_value(vm, make_int(0));
break;
}
push_value(vm, make_int(1)); push_value(vm, make_int(1));
#else #else
Value a = pop_value(vm); free_value(a); Value a = pop_value(vm);
Value b = pop_value(vm); free_value(b); free_value(a);
Value b = pop_value(vm);
free_value(b);
push_value(vm, make_int(0)); push_value(vm, make_int(0));
#endif #endif
break; break;

View file

@ -6,10 +6,17 @@ case OP_CURL_GET: {
Value vurl = pop_value(vm); Value vurl = pop_value(vm);
char *url = value_to_string_alloc(&vurl); char *url = value_to_string_alloc(&vurl);
free_value(vurl); free_value(vurl);
if (!url) { push_value(vm, make_string("")); break; } if (!url) {
FunCurlBuf buf = { NULL, 0 }; push_value(vm, make_string(""));
break;
}
FunCurlBuf buf = {NULL, 0};
CURL *h = curl_easy_init(); CURL *h = curl_easy_init();
if (!h) { free(url); push_value(vm, make_string("")); break; } if (!h) {
free(url);
push_value(vm, make_string(""));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url); curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb); curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
@ -26,7 +33,8 @@ case OP_CURL_GET: {
if (buf.d) free(buf.d); if (buf.d) free(buf.d);
push_value(vm, s); push_value(vm, s);
#else #else
Value v = pop_value(vm); free_value(v); Value v = pop_value(vm);
free_value(v);
push_value(vm, make_string("")); push_value(vm, make_string(""));
#endif #endif
break; break;

View file

@ -9,11 +9,20 @@ case OP_CURL_POST: {
char *body = value_to_string_alloc(&vbody); char *body = value_to_string_alloc(&vbody);
free_value(vurl); free_value(vurl);
free_value(vbody); free_value(vbody);
if (!url) { if (body) free(body); push_value(vm, make_string("")); break; } if (!url) {
if (body) free(body);
push_value(vm, make_string(""));
break;
}
if (!body) body = strdup(""); if (!body) body = strdup("");
FunCurlBuf buf = { NULL, 0 }; FunCurlBuf buf = {NULL, 0};
CURL *h = curl_easy_init(); CURL *h = curl_easy_init();
if (!h) { free(url); free(body); push_value(vm, make_string("")); break; } if (!h) {
free(url);
free(body);
push_value(vm, make_string(""));
break;
}
curl_easy_setopt(h, CURLOPT_URL, url); curl_easy_setopt(h, CURLOPT_URL, url);
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(h, CURLOPT_POST, 1L); curl_easy_setopt(h, CURLOPT_POST, 1L);
@ -33,8 +42,10 @@ case OP_CURL_POST: {
if (buf.d) free(buf.d); if (buf.d) free(buf.d);
push_value(vm, s); push_value(vm, s);
#else #else
Value a = pop_value(vm); free_value(a); Value a = pop_value(vm);
Value b = pop_value(vm); free_value(b); free_value(a);
Value b = pop_value(vm);
free_value(b);
push_value(vm, make_string("")); push_value(vm, make_string(""));
#endif #endif
break; break;

View file

@ -1,5 +1,5 @@
/* /*
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -7,7 +7,7 @@
* https://opensource.org/license/apache-2-0 * https://opensource.org/license/apache-2-0
*/ */
/** /**
* Implements OP_ECHO: print top-of-stack value without trailing newline. * 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, * 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. * so the CLI can render echo output together with following print output.

View file

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

View file

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

View file

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

View file

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

View file

@ -9,23 +9,23 @@
#ifdef FUN_WITH_INI #ifdef FUN_WITH_INI
#if defined(__has_include) #if defined(__has_include)
# if __has_include(<iniparser/iniparser.h>) #if __has_include(<iniparser/iniparser.h>)
# include <iniparser/iniparser.h> #include <iniparser/dictionary.h>
# include <iniparser/dictionary.h> #include <iniparser/iniparser.h>
# elif __has_include(<iniparser.h>) #elif __has_include(<iniparser.h>)
# include <iniparser.h> #include <dictionary.h>
# include <dictionary.h> #include <iniparser.h>
# else
# error "iniparser headers not found"
# endif
#else #else
# include <iniparser/iniparser.h> #error "iniparser headers not found"
# include <iniparser/dictionary.h> #endif
#else
#include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif #endif
#include <ctype.h> #include <ctype.h>
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "handles.h" #include "handles.h"
@ -33,19 +33,23 @@ IniSlot g_ini[64];
int ini_alloc_handle(dictionary *d) { int ini_alloc_handle(dictionary *d) {
if (!d) return 0; if (!d) return 0;
for (int i = 1; i < (int)(sizeof(g_ini)/sizeof(g_ini[0])); ++i) { 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 (!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) { 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; if (h > 0 && h < (int)(sizeof(g_ini) / sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
return NULL; return NULL;
} }
int ini_free_handle(int h) { 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 (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); if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
g_ini[h].dict = NULL; g_ini[h].dict = NULL;
g_ini[h].in_use = 0; g_ini[h].in_use = 0;

View file

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

View file

@ -19,7 +19,9 @@ case OP_INI_LOAD: {
dictionary *d = iniparser_load(path); dictionary *d = iniparser_load(path);
if (d) { if (d) {
h = ini_alloc_handle(d); h = ini_alloc_handle(d);
if (!h) { iniparser_freedict(d); } if (!h) {
iniparser_freedict(d);
}
} }
} }
free_value(vpath); free_value(vpath);

View file

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

View file

@ -16,17 +16,23 @@ case OP_INI_SET: {
Value vkey = pop_value(vm); Value vkey = pop_value(vm);
Value vsec = pop_value(vm); Value vsec = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0); dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
const char *key = (vkey.type==VAL_STRING)?vkey.s:NULL; const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL; const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
int ok = 0; int ok = 0;
if (d && sec && key) { if (d && sec && key) {
char *valstr = value_to_string_alloc(&vval); char *valstr = value_to_string_alloc(&vval);
if (valstr) { if (valstr) {
char full[1024]; char alt[1024]; char full[1024];
char alt[1024];
ini_make_full_key(full, sizeof(full), sec, key); ini_make_full_key(full, sizeof(full), sec, key);
memcpy(alt, full, sizeof(alt)); memcpy(alt, full, sizeof(alt));
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } } 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 */ /* iniparser 4.x does not expose iniparser_set; use dictionary_set */
if (dictionary_set(d, full, valstr) == 0) { if (dictionary_set(d, full, valstr) == 0) {
ok = 1; /* 0 means success */ ok = 1; /* 0 means success */
@ -36,7 +42,10 @@ case OP_INI_SET: {
free(valstr); free(valstr);
} }
} }
free_value(vval); free_value(vkey); free_value(vsec); free_value(vh); free_value(vval);
free_value(vkey);
free_value(vsec);
free_value(vh);
push_value(vm, make_int(ok)); push_value(vm, make_int(ok));
break; break;
} }

View file

@ -28,7 +28,9 @@ case OP_INI_GET_STRING: {
Value vkey = pop_value(vm); Value vkey = pop_value(vm);
Value vsec = pop_value(vm); Value vsec = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey); free_value(vh);
free_value(vsec);
free_value(vkey);
/* cannot convert here; return empty string */ /* cannot convert here; return empty string */
push_value(vm, make_string("")); push_value(vm, make_string(""));
free_value(vdef); free_value(vdef);
@ -41,7 +43,9 @@ case OP_INI_GET_INT: {
Value vkey = pop_value(vm); Value vkey = pop_value(vm);
Value vsec = pop_value(vm); Value vsec = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey); free_value(vh);
free_value(vsec);
free_value(vkey);
(void)vdef; /* unused */ (void)vdef; /* unused */
push_value(vm, make_int(0)); push_value(vm, make_int(0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
@ -53,7 +57,9 @@ case OP_INI_GET_DOUBLE: {
Value vkey = pop_value(vm); Value vkey = pop_value(vm);
Value vsec = pop_value(vm); Value vsec = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey); free_value(vh);
free_value(vsec);
free_value(vkey);
(void)vdef; /* unused */ (void)vdef; /* unused */
push_value(vm, make_float(0.0)); push_value(vm, make_float(0.0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
@ -65,7 +71,9 @@ case OP_INI_GET_BOOL: {
Value vkey = pop_value(vm); Value vkey = pop_value(vm);
Value vsec = pop_value(vm); Value vsec = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey); free_value(vh);
free_value(vsec);
free_value(vkey);
(void)vdef; /* unused */ (void)vdef; /* unused */
push_value(vm, make_int(0)); push_value(vm, make_int(0));
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
@ -78,7 +86,10 @@ case OP_INI_SET: {
Value vkey = pop_value(vm); Value vkey = pop_value(vm);
Value vsec = pop_value(vm); Value vsec = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey); free_value(vval); 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"); fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0)); push_value(vm, make_int(0));
break; break;
@ -88,7 +99,9 @@ case OP_INI_UNSET: {
Value vkey = pop_value(vm); Value vkey = pop_value(vm);
Value vsec = pop_value(vm); Value vsec = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
free_value(vh); free_value(vsec); free_value(vkey); 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"); fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0)); push_value(vm, make_int(0));
break; break;
@ -97,7 +110,8 @@ case OP_INI_UNSET: {
case OP_INI_SAVE: { case OP_INI_SAVE: {
Value vpath = pop_value(vm); Value vpath = pop_value(vm);
Value vh = pop_value(vm); Value vh = pop_value(vm);
free_value(vh); free_value(vpath); free_value(vh);
free_value(vpath);
fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n"); fun_vm_fprintf(stderr, "Runtime error: INI support disabled (rebuild with -DFUN_WITH_INI=ON)\n");
push_value(vm, make_int(0)); push_value(vm, make_int(0));
break; break;

View file

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

View file

@ -49,7 +49,7 @@ case OP_INPUT_LINE: {
/* read a line from stdin, dynamically grow buffer */ /* read a line from stdin, dynamically grow buffer */
size_t cap = 128; size_t cap = 128;
size_t len = 0; size_t len = 0;
char *buf = (char*)malloc(cap); char *buf = (char *)malloc(cap);
if (!buf) { if (!buf) {
fprintf(stderr, "Runtime error: out of memory reading input"); fprintf(stderr, "Runtime error: out of memory reading input");
push_value(vm, make_string("")); push_value(vm, make_string(""));
@ -72,7 +72,7 @@ case OP_INPUT_LINE: {
} }
if (len + 1 >= cap) { if (len + 1 >= cap) {
cap *= 2; cap *= 2;
char *nb = (char*)realloc(buf, cap); char *nb = (char *)realloc(buf, cap);
if (!nb) { if (!nb) {
free(buf); free(buf);
fprintf(stderr, "Runtime error: out of memory reading input"); fprintf(stderr, "Runtime error: out of memory reading input");
@ -86,7 +86,7 @@ case OP_INPUT_LINE: {
/* null-terminate */ /* null-terminate */
if (len + 1 >= cap) { if (len + 1 >= cap) {
char *nb = (char*)realloc(buf, len + 1); char *nb = (char *)realloc(buf, len + 1);
if (!nb) { if (!nb) {
free(buf); free(buf);
fprintf(stderr, "Runtime error: out of memory finalizing input"); fprintf(stderr, "Runtime error: out of memory finalizing input");

View file

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

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file write_file.c * @file write_file.c
* @brief Implements the OP_WRITE_FILE opcode for writing to a file in the VM. * @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. * This file handles the OP_WRITE_FILE instruction, which writes data to a file.
@ -34,7 +34,10 @@
case OP_WRITE_FILE: { case OP_WRITE_FILE: {
Value data = pop_value(vm); Value data = pop_value(vm);
Value path = 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); } 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 : ""; const char *p = path.s ? path.s : "";
FILE *f = fopen(p, "wb"); FILE *f = fopen(p, "wb");
int ok = 0; int ok = 0;

View file

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

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -15,7 +15,10 @@ case OP_JSON_PARSE: {
Value text = pop_value(vm); Value text = pop_value(vm);
char *s = value_to_string_alloc(&text); char *s = value_to_string_alloc(&text);
free_value(text); free_value(text);
if (!s) { push_value(vm, make_nil()); break; } if (!s) {
push_value(vm, make_nil());
break;
}
struct json_tokener *tok = json_tokener_new(); struct json_tokener *tok = json_tokener_new();
json_object *root = json_tokener_parse_ex(tok, s, (int)strlen(s)); json_object *root = json_tokener_parse_ex(tok, s, (int)strlen(s));
enum json_tokener_error jerr = json_tokener_get_error(tok); enum json_tokener_error jerr = json_tokener_get_error(tok);

View file

@ -1,5 +1,5 @@
/** /**
* This file is part of the Fun programming language. * This file is part of the Fun programming language.
* https://fun-lang.xyz/ * https://fun-lang.xyz/
* *
* Copyright 2025 Johannes Findeisen <you@hanez.org> * Copyright 2025 Johannes Findeisen <you@hanez.org>
@ -24,8 +24,10 @@ case OP_JSON_STRINGIFY: {
free_value(any); free_value(any);
#else #else
/* Fallback: consume two args, push "null" */ /* Fallback: consume two args, push "null" */
Value vpretty = pop_value(vm); free_value(vpretty); Value vpretty = pop_value(vm);
Value any = pop_value(vm); free_value(any); free_value(vpretty);
Value any = pop_value(vm);
free_value(any);
push_value(vm, make_string("null")); push_value(vm, make_string("null"));
#endif #endif
break; break;

View file

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

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* @file len.c * @file len.c
* @brief Implements the OP_LEN opcode for getting the length of arrays or strings in the VM. * @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. * This file handles the OP_LEN instruction, which retrieves the length of an array or string.

View file

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

View file

@ -16,10 +16,16 @@ case OP_LIBRESSL_RIPEMD160: {
Value vdata = pop_value(vm); Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata); char *s = value_to_string_alloc(&vdata);
free_value(vdata); free_value(vdata);
if (!s) { push_value(vm, make_string("")); break; } if (!s) {
char *hex = fun_libressl_ripemd160_hex((const unsigned char*)s, strlen(s)); push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_ripemd160_hex((const unsigned char *)s, strlen(s));
free(s); free(s);
if (!hex) { push_value(vm, make_string("")); break; } if (!hex) {
push_value(vm, make_string(""));
break;
}
Value out = make_string(hex); Value out = make_string(hex);
free(hex); free(hex);
push_value(vm, out); push_value(vm, out);

View file

@ -16,10 +16,16 @@ case OP_LIBRESSL_SHA256: {
Value vdata = pop_value(vm); Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata); char *s = value_to_string_alloc(&vdata);
free_value(vdata); free_value(vdata);
if (!s) { push_value(vm, make_string("")); break; } if (!s) {
char *hex = fun_libressl_sha256_hex((const unsigned char*)s, strlen(s)); push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_sha256_hex((const unsigned char *)s, strlen(s));
free(s); free(s);
if (!hex) { push_value(vm, make_string("")); break; } if (!hex) {
push_value(vm, make_string(""));
break;
}
Value out = make_string(hex); Value out = make_string(hex);
free(hex); free(hex);
push_value(vm, out); push_value(vm, out);

View file

@ -16,10 +16,16 @@ case OP_LIBRESSL_SHA512: {
Value vdata = pop_value(vm); Value vdata = pop_value(vm);
char *s = value_to_string_alloc(&vdata); char *s = value_to_string_alloc(&vdata);
free_value(vdata); free_value(vdata);
if (!s) { push_value(vm, make_string("")); break; } if (!s) {
char *hex = fun_libressl_sha512_hex((const unsigned char*)s, strlen(s)); push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_sha512_hex((const unsigned char *)s, strlen(s));
free(s); free(s);
if (!hex) { push_value(vm, make_string("")); break; } if (!hex) {
push_value(vm, make_string(""));
break;
}
Value out = make_string(hex); Value out = make_string(hex);
free(hex); free(hex);
push_value(vm, out); push_value(vm, out);

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