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

@ -8,8 +8,8 @@
*/ */
#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));
@ -55,171 +55,336 @@ void bytecode_free(Bytecode *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 {

9
src/external/curl.c vendored
View file

@ -12,14 +12,19 @@
/* 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) {

6
src/external/ini.c vendored
View file

@ -12,17 +12,17 @@
#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 #else
#error "iniparser headers not found" #error "iniparser headers not found"
#endif #endif
#else #else
# include <iniparser/iniparser.h>
#include <iniparser/dictionary.h> #include <iniparser/dictionary.h>
#include <iniparser/iniparser.h>
#endif #endif
#include "vm/ini/handles.h" #include "vm/ini/handles.h"
#endif #endif

33
src/external/json.c vendored
View file

@ -23,11 +23,16 @@ 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) {
@ -40,7 +45,8 @@ static Value json_to_fun(json_object *j) {
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;
} }
@ -58,11 +64,16 @@ 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

@ -49,9 +49,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];
@ -84,9 +90,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];
@ -119,9 +131,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];
@ -154,9 +172,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];

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;
@ -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

@ -45,9 +45,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];
@ -81,9 +87,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];
@ -117,9 +129,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];
@ -155,9 +173,15 @@ 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) {
free(digest);
return NULL;
}
char *hex = (char *)malloc((size_t)dlen * 2 + 1); char *hex = (char *)malloc((size_t)dlen * 2 + 1);
if (!hex) { free(digest); return NULL; } 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];

13
src/external/pcsc.c vendored
View file

@ -46,14 +46,23 @@ 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;
} }

10
src/external/sqlite.c vendored
View file

@ -35,14 +35,20 @@ static SqlHandle* sql_reg_add(sqlite3 *db) {
} }
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;
} }
} }

20
src/external/tcltk.c vendored
View file

@ -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

22
src/external/xml2.c vendored
View file

@ -13,15 +13,25 @@
#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;
} }
@ -39,7 +49,11 @@ 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;
} }

View file

@ -14,8 +14,8 @@
*/ */
#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>

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

@ -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;
} }
@ -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

@ -37,7 +37,8 @@ Value make_map_empty(void) {
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)
ncap *= 2;
char **nkeys = (char **)realloc(m->keys, sizeof(char *) * ncap); char **nkeys = (char **)realloc(m->keys, sizeof(char *) * ncap);
Value *nvals = (Value *)realloc(m->vals, sizeof(Value) * ncap); Value *nvals = (Value *)realloc(m->vals, sizeof(Value) * ncap);
if (!nkeys || !nvals) return 0; if (!nkeys || !nvals) return 0;
@ -48,7 +49,10 @@ 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) {
free_value(v);
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) {
@ -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++;
@ -95,7 +102,8 @@ Value map_keys_array(const Value *vm) {
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;
} }
@ -110,7 +118,8 @@ Value map_values_array(const Value *vm) {
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;
} }
@ -98,25 +116,45 @@ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t
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;
@ -124,7 +162,10 @@ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t
} }
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,7 +190,8 @@ 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;
@ -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');
@ -229,7 +283,8 @@ 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)
nc *= 2;
char *nb = (char *)xrealloc(sb->buf, nc); char *nb = (char *)xrealloc(sb->buf, nc);
if (!nb) return; if (!nb) return;
sb->buf = nb; sb->buf = nb;
@ -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;
@ -300,8 +356,12 @@ static void collect_exports_top_level(const char *text, NameList *out) {
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];
@ -434,7 +525,8 @@ static char *preprocess_includes_internal(const char *src, int depth) {
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,13 +534,15 @@ 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);
@ -458,20 +552,24 @@ static char *preprocess_includes_internal(const char *src, int depth) {
/* 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

@ -49,7 +49,8 @@ Value string_split_to_array(const char *s, const char *sep) {
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;
} }
@ -83,7 +84,8 @@ Value string_split_to_array(const char *s, const char *sep) {
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;
} }
@ -113,7 +115,8 @@ char *array_join_with_sep(const Value *v, const char *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() {

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,7 +74,6 @@ 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));
@ -130,7 +131,8 @@ 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)
cap *= 2;
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * cap); 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 */
@ -148,7 +150,10 @@ int array_push(Value *v, Value newElem) {
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;
@ -160,8 +165,10 @@ int array_pop(Value *v, Value *out) {
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;
} }
@ -172,7 +179,10 @@ int array_insert(Value *v, int index, Value newElem) {
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) {
@ -187,8 +197,10 @@ 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];
@ -221,8 +233,10 @@ Value array_concat(const Value *av, const Value *bv) {
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);
@ -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;
} }
} }

241
src/vm.c
View file

@ -18,21 +18,21 @@
#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>
@ -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);
@ -112,14 +112,18 @@ static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) {
/* 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;
@ -310,15 +330,24 @@ Dev tips:
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) {
@ -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;
@ -657,12 +689,14 @@ void vm_run(VM *vm, Bytecode *entry) {
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]);
@ -716,13 +750,13 @@ void vm_run(VM *vm, Bytecode *entry) {
/* Bitwise and shifts/rotates */ /* Bitwise and shifts/rotates */
#include "vm/bitwise/band.c" #include "vm/bitwise/band.c"
#include "vm/bitwise/bnot.c"
#include "vm/bitwise/bor.c" #include "vm/bitwise/bor.c"
#include "vm/bitwise/bxor.c" #include "vm/bitwise/bxor.c"
#include "vm/bitwise/bnot.c"
#include "vm/bitwise/shl.c"
#include "vm/bitwise/shr.c"
#include "vm/bitwise/rol.c" #include "vm/bitwise/rol.c"
#include "vm/bitwise/ror.c" #include "vm/bitwise/ror.c"
#include "vm/bitwise/shl.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"
@ -743,9 +777,9 @@ void vm_run(VM *vm, Bytecode *entry) {
#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/input_line.c"
#include "vm/io/read_file.c" #include "vm/io/read_file.c"
#include "vm/io/write_file.c" #include "vm/io/write_file.c"
#include "vm/io/input_line.c"
#include "vm/logic/and.c" #include "vm/logic/and.c"
#include "vm/logic/eq.c" #include "vm/logic/eq.c"
@ -763,155 +797,155 @@ void vm_run(VM *vm, Bytecode *entry) {
#include "vm/maps/values.c" #include "vm/maps/values.c"
#include "vm/math/abs.c" #include "vm/math/abs.c"
#include "vm/math/ceil.c"
#include "vm/math/clamp.c" #include "vm/math/clamp.c"
#include "vm/math/max.c" #include "vm/math/cos.c"
#include "vm/math/min.c" #include "vm/math/exp.c"
#include "vm/math/floor.c"
#include "vm/math/fmax.c" #include "vm/math/fmax.c"
#include "vm/math/fmin.c" #include "vm/math/fmin.c"
#include "vm/math/mod.c" #include "vm/math/gcd.c"
#include "vm/math/pow.c" #include "vm/math/isqrt.c"
#include "vm/math/floor.c" #include "vm/math/lcm.c"
#include "vm/math/ceil.c"
#include "vm/math/trunc.c"
#include "vm/math/round.c"
#include "vm/math/sin.c"
#include "vm/math/cos.c"
#include "vm/math/tan.c"
#include "vm/math/exp.c"
#include "vm/math/log.c" #include "vm/math/log.c"
#include "vm/math/log10.c" #include "vm/math/log10.c"
#include "vm/math/sqrt.c" #include "vm/math/max.c"
#include "vm/math/min.c"
#include "vm/math/mod.c"
#include "vm/math/pow.c"
#include "vm/math/random_int.c" #include "vm/math/random_int.c"
#include "vm/math/random_seed.c" #include "vm/math/random_seed.c"
#include "vm/math/gcd.c" #include "vm/math/round.c"
#include "vm/math/lcm.c"
#include "vm/math/isqrt.c"
#include "vm/math/sign.c" #include "vm/math/sign.c"
#include "vm/math/sin.c"
#include "vm/math/sqrt.c"
#include "vm/math/tan.c"
#include "vm/math/trunc.c"
/* Rust FFI demo opcode(s) */ /* Rust FFI demo opcode(s) */
#include "vm/rust/get_sp.c"
#include "vm/rust/hello.c" #include "vm/rust/hello.c"
#include "vm/rust/hello_args.c" #include "vm/rust/hello_args.c"
#include "vm/rust/hello_args_return.c" #include "vm/rust/hello_args_return.c"
#include "vm/rust/get_sp.c"
#include "vm/rust/set_exit.c" #include "vm/rust/set_exit.c"
#include "vm/os/clock_mono_ms.c"
#include "vm/os/date_format.c"
#include "vm/os/env.c" #include "vm/os/env.c"
#include "vm/os/env_all.c" #include "vm/os/env_all.c"
#include "vm/os/fun_version.c" #include "vm/os/fun_version.c"
#include "vm/os/proc_run.c"
#include "vm/os/proc_system.c"
#include "vm/os/random_number.c"
#include "vm/os/serial_close.c"
#include "vm/os/serial_config.c"
#include "vm/os/serial_open.c"
#include "vm/os/serial_recv.c"
#include "vm/os/serial_send.c"
#include "vm/os/sleep_ms.c" #include "vm/os/sleep_ms.c"
#include "vm/os/thread_join.c" #include "vm/os/thread_join.c"
#include "vm/os/thread_spawn.c" #include "vm/os/thread_spawn.c"
#include "vm/os/proc_run.c"
#include "vm/os/proc_system.c"
#include "vm/os/time_now_ms.c" #include "vm/os/time_now_ms.c"
#include "vm/os/clock_mono_ms.c"
#include "vm/os/date_format.c"
#include "vm/os/random_number.c"
#include "vm/os/serial_open.c"
#include "vm/os/serial_config.c"
#include "vm/os/serial_send.c"
#include "vm/os/serial_recv.c"
#include "vm/os/serial_close.c"
/* Socket ops */ /* Socket ops */
#include "vm/os/socket_tcp_listen.c" #include "vm/os/socket_close.c"
#include "vm/os/socket_recv.c"
#include "vm/os/socket_send.c"
#include "vm/os/socket_tcp_accept.c" #include "vm/os/socket_tcp_accept.c"
#include "vm/os/socket_tcp_connect.c" #include "vm/os/socket_tcp_connect.c"
#include "vm/os/socket_send.c" #include "vm/os/socket_tcp_listen.c"
#include "vm/os/socket_recv.c"
#include "vm/os/socket_close.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/release.c"
#include "vm/pcsc/list_readers.c"
#include "vm/pcsc/connect.c" #include "vm/pcsc/connect.c"
#include "vm/pcsc/disconnect.c" #include "vm/pcsc/disconnect.c"
#include "vm/pcsc/establish.c"
#include "vm/pcsc/list_readers.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/from_file.c"
#include "vm/json/parse.c" #include "vm/json/parse.c"
#include "vm/json/stringify.c" #include "vm/json/stringify.c"
#include "vm/json/from_file.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/name.c"
#include "vm/xml/parse.c" #include "vm/xml/parse.c"
#include "vm/xml/root.c" #include "vm/xml/root.c"
#include "vm/xml/name.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_string.c"
#include "vm/ini/get_int.c"
#include "vm/ini/get_double.c"
#include "vm/ini/get_bool.c" #include "vm/ini/get_bool.c"
#include "vm/ini/get_double.c"
#include "vm/ini/get_int.c"
#include "vm/ini/get_string.c"
#include "vm/ini/load.c"
#include "vm/ini/save.c"
#include "vm/ini/set.c" #include "vm/ini/set.c"
#include "vm/ini/unset.c" #include "vm/ini/unset.c"
#include "vm/ini/save.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/download.c"
#include "vm/curl/get.c" #include "vm/curl/get.c"
#include "vm/curl/post.c" #include "vm/curl/post.c"
#include "vm/curl/download.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/ripemd160.c"
#include "vm/openssl/sha256.c" #include "vm/openssl/sha256.c"
#include "vm/openssl/sha512.c" #include "vm/openssl/sha512.c"
#include "vm/openssl/ripemd160.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/ripemd160.c"
#include "vm/libressl/sha256.c" #include "vm/libressl/sha256.c"
#include "vm/libressl/sha512.c" #include "vm/libressl/sha512.c"
#include "vm/libressl/ripemd160.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/result.c"
#include "vm/tk/loop.c"
#include "vm/tk/wm_title.c"
#include "vm/tk/label.c"
#include "vm/tk/button.c"
#include "vm/tk/pack.c"
#include "vm/tk/bind.c" #include "vm/tk/bind.c"
#include "vm/tk/button.c"
#include "vm/tk/eval.c"
#include "vm/tk/label.c"
#include "vm/tk/loop.c"
#include "vm/tk/pack.c"
#include "vm/tk/result.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/shutdown.c"
#include "vm/notcurses/clear.c" #include "vm/notcurses/clear.c"
#include "vm/notcurses/draw_text.c" #include "vm/notcurses/draw_text.c"
#include "vm/notcurses/getch.c" #include "vm/notcurses/getch.c"
#include "vm/notcurses/init.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
@ -933,44 +967,45 @@ void vm_run(VM *vm, Bytecode *entry) {
/* 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/match.c"
#include "vm/pcre2/findall.c" #include "vm/pcre2/findall.c"
#include "vm/pcre2/match.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/cast.c"
#include "vm/echo.c"
#include "vm/len.c" #include "vm/len.c"
#include "vm/line.c" #include "vm/line.c"
#include "vm/os/list_dir.c"
#include "vm/print.c" #include "vm/print.c"
#include "vm/echo.c" #include "vm/sclamp.c"
#include "vm/to_number.c" #include "vm/to_number.c"
#include "vm/to_string.c" #include "vm/to_string.c"
#include "vm/cast.c"
#include "vm/typeof.c" #include "vm/typeof.c"
#include "vm/uclamp.c" #include "vm/uclamp.c"
#include "vm/sclamp.c"
#include "vm/os/list_dir.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

@ -61,8 +61,7 @@ static const char *opcode_names[] = {
/* 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

@ -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

@ -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

@ -40,7 +40,10 @@ case OP_MAKE_ARRAY: {
} }
/* 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

@ -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

@ -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

@ -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) {
push_value(vm, make_string(""));
break;
}
FunCurlBuf buf = {NULL, 0}; 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

@ -23,10 +23,16 @@ case OP_INI_GET_BOOL: {
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) {
@ -35,27 +41,37 @@ case OP_INI_GET_BOOL: {
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];
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) { 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;
long v = strtol(lb, &endp, 10);
outb = (endp && endp != lb) ? (v != 0) : def; 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

@ -23,10 +23,16 @@ case OP_INI_GET_DOUBLE: {
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) {
@ -34,18 +40,26 @@ case OP_INI_GET_DOUBLE: {
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

@ -23,10 +23,16 @@ case OP_INI_GET_INT: {
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) {
@ -35,19 +41,27 @@ case OP_INI_GET_INT: {
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

@ -23,14 +23,19 @@ case OP_INI_GET_STRING: {
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

@ -10,22 +10,22 @@
#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 #else
#error "iniparser headers not found" #error "iniparser headers not found"
#endif #endif
#else #else
# include <iniparser/iniparser.h>
#include <iniparser/dictionary.h> #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"
@ -34,7 +34,11 @@ 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;
} }

View file

@ -15,21 +15,24 @@
#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 #else
#error "iniparser headers not found" #error "iniparser headers not found"
#endif #endif
#else #else
# include <iniparser/iniparser.h>
#include <iniparser/dictionary.h> #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];

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

@ -19,9 +19,14 @@ case OP_INI_SAVE: {
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

@ -23,10 +23,16 @@ case OP_INI_SET: {
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

@ -20,16 +20,24 @@ case OP_INI_UNSET: {
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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -16,10 +16,16 @@ 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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_md5_hex((const unsigned char *)s, strlen(s)); 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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_ripemd160_hex((const unsigned char *)s, strlen(s)); 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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_sha256_hex((const unsigned char *)s, strlen(s)); 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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_libressl_sha512_hex((const unsigned char *)s, strlen(s)); 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);

View file

@ -25,7 +25,8 @@ case OP_LIBSQL_CLOSE: {
} }
push_value(vm, make_nil()); push_value(vm, make_nil());
#else #else
Value v = pop_value(vm); free_value(v); Value v = pop_value(vm);
free_value(v);
push_value(vm, make_nil()); push_value(vm, make_nil());
#endif #endif
break; break;

View file

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

View file

@ -17,7 +17,10 @@ case OP_LIBSQL_OPEN: {
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_int(0)); break; } if (!path) {
push_value(vm, make_int(0));
break;
}
sqlite3 *db = NULL; sqlite3 *db = NULL;
int rc = sqlite3_open(path, &db); int rc = sqlite3_open(path, &db);
free(path); free(path);
@ -27,10 +30,15 @@ case OP_LIBSQL_OPEN: {
break; break;
} }
LibSqlHandle *h = libsql_reg_add(db); LibSqlHandle *h = libsql_reg_add(db);
if (!h) { sqlite3_close(db); push_value(vm, make_int(0)); break; } if (!h) {
sqlite3_close(db);
push_value(vm, make_int(0));
break;
}
push_value(vm, make_int(h->id)); push_value(vm, make_int(h->id));
#else #else
Value v = pop_value(vm); free_value(v); Value v = pop_value(vm);
free_value(v);
push_value(vm, make_int(0)); push_value(vm, make_int(0));
#endif #endif
break; break;

View file

@ -21,7 +21,11 @@ case OP_LIBSQL_QUERY: {
free_value(vh); free_value(vh);
free_value(vsql); free_value(vsql);
LibSqlHandle *h = libsql_reg_get(hid); LibSqlHandle *h = libsql_reg_get(hid);
if (!h || !h->db || !sql) { if (sql) free(sql); push_value(vm, make_array_from_values(NULL, 0)); break; } if (!h || !h->db || !sql) {
if (sql) free(sql);
push_value(vm, make_array_from_values(NULL, 0));
break;
}
sqlite3_stmt *stmt = NULL; sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(h->db, sql, -1, &stmt, NULL) != SQLITE_OK) { if (sqlite3_prepare_v2(h->db, sql, -1, &stmt, NULL) != SQLITE_OK) {
free(sql); free(sql);
@ -38,11 +42,21 @@ case OP_LIBSQL_QUERY: {
int type = sqlite3_column_type(stmt, i); int type = sqlite3_column_type(stmt, i);
Value kv; Value kv;
switch (type) { switch (type) {
case SQLITE_INTEGER: kv = make_int((int64_t)sqlite3_column_int64(stmt, i)); break; case SQLITE_INTEGER:
case SQLITE_FLOAT: kv = make_float(sqlite3_column_double(stmt, i)); break; kv = make_int((int64_t)sqlite3_column_int64(stmt, i));
case SQLITE_TEXT: kv = make_string((const char*)sqlite3_column_text(stmt, i)); break; break;
case SQLITE_NULL: kv = make_nil(); break; case SQLITE_FLOAT:
default: kv = make_nil(); break; /* ignore blobs for now */ kv = make_float(sqlite3_column_double(stmt, i));
break;
case SQLITE_TEXT:
kv = make_string((const char *)sqlite3_column_text(stmt, i));
break;
case SQLITE_NULL:
kv = make_nil();
break;
default:
kv = make_nil();
break; /* ignore blobs for now */
} }
(void)map_set(&row, name ? name : "", kv); (void)map_set(&row, name ? name : "", kv);
} }
@ -52,8 +66,10 @@ case OP_LIBSQL_QUERY: {
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
push_value(vm, rows); push_value(vm, rows);
#else #else
Value v1 = pop_value(vm); free_value(v1); Value v1 = pop_value(vm);
Value v2 = pop_value(vm); free_value(v2); free_value(v1);
Value v2 = pop_value(vm);
free_value(v2);
push_value(vm, make_array_from_values(NULL, 0)); push_value(vm, make_array_from_values(NULL, 0));
#endif #endif
break; break;

View file

@ -37,12 +37,24 @@ case OP_EQ: {
int eq = 0; int eq = 0;
if (a.type == b.type) { if (a.type == b.type) {
switch (a.type) { switch (a.type) {
case VAL_INT: eq = (a.i == b.i); break; case VAL_INT:
case VAL_BOOL: eq = ((a.i != 0) == (b.i != 0)); break; eq = (a.i == b.i);
case VAL_STRING: eq = (a.s && b.s) ? (strcmp(a.s, b.s) == 0) : (a.s == b.s); break; break;
case VAL_FUNCTION: eq = (a.fn == b.fn); break; case VAL_BOOL:
case VAL_NIL: eq = 1; break; eq = ((a.i != 0) == (b.i != 0));
default: eq = 0; break; break;
case VAL_STRING:
eq = (a.s && b.s) ? (strcmp(a.s, b.s) == 0) : (a.s == b.s);
break;
case VAL_FUNCTION:
eq = (a.fn == b.fn);
break;
case VAL_NIL:
eq = 1;
break;
default:
eq = 0;
break;
} }
} else { } else {
/* interop: bool vs int (0/1) */ /* interop: bool vs int (0/1) */
@ -55,6 +67,7 @@ case OP_EQ: {
} }
} }
push_value(vm, make_bool(eq)); push_value(vm, make_bool(eq));
free_value(a); free_value(b); free_value(a);
free_value(b);
break; break;
} }

View file

@ -40,6 +40,7 @@ case OP_GT: {
exit(1); exit(1);
} }
push_value(vm, make_int(a.i > b.i ? 1 : 0)); push_value(vm, make_int(a.i > b.i ? 1 : 0));
free_value(a); free_value(b); free_value(a);
free_value(b);
break; break;
} }

View file

@ -39,6 +39,7 @@ case OP_GTE: {
exit(1); exit(1);
} }
push_value(vm, make_int(a.i >= b.i ? 1 : 0)); push_value(vm, make_int(a.i >= b.i ? 1 : 0));
free_value(a); free_value(b); free_value(a);
free_value(b);
break; break;
} }

View file

@ -37,12 +37,24 @@ case OP_NEQ: {
int neq = 1; int neq = 1;
if (a.type == b.type) { if (a.type == b.type) {
switch (a.type) { switch (a.type) {
case VAL_INT: neq = (a.i != b.i); break; case VAL_INT:
case VAL_BOOL: neq = ((a.i != 0) != (b.i != 0)); break; neq = (a.i != b.i);
case VAL_STRING: neq = (a.s && b.s) ? (strcmp(a.s, b.s) != 0) : (a.s != b.s); break; break;
case VAL_FUNCTION: neq = (a.fn != b.fn); break; case VAL_BOOL:
case VAL_NIL: neq = 0; break; neq = ((a.i != 0) != (b.i != 0));
default: neq = 1; break; break;
case VAL_STRING:
neq = (a.s && b.s) ? (strcmp(a.s, b.s) != 0) : (a.s != b.s);
break;
case VAL_FUNCTION:
neq = (a.fn != b.fn);
break;
case VAL_NIL:
neq = 0;
break;
default:
neq = 1;
break;
} }
} else { } else {
/* interop: bool vs int (0/1) */ /* interop: bool vs int (0/1) */
@ -55,6 +67,7 @@ case OP_NEQ: {
} }
} }
push_value(vm, make_bool(neq)); push_value(vm, make_bool(neq));
free_value(a); free_value(b); free_value(a);
free_value(b);
break; break;
} }

View file

@ -28,9 +28,13 @@
case OP_HAS_KEY: { case OP_HAS_KEY: {
Value key = pop_value(vm); Value key = pop_value(vm);
Value m = pop_value(vm); Value m = pop_value(vm);
if (m.type != VAL_MAP || key.type != VAL_STRING) { fprintf(stderr, "HAS_KEY expects (map, string)\n"); exit(1); } if (m.type != VAL_MAP || key.type != VAL_STRING) {
fprintf(stderr, "HAS_KEY expects (map, string)\n");
exit(1);
}
int ok = map_has(&m, key.s ? key.s : ""); int ok = map_has(&m, key.s ? key.s : "");
free_value(m); free_value(key); free_value(m);
free_value(key);
push_value(vm, make_int(ok ? 1 : 0)); push_value(vm, make_int(ok ? 1 : 0));
break; break;
} }

View file

@ -33,7 +33,10 @@
case OP_KEYS: { case OP_KEYS: {
Value m = pop_value(vm); Value m = pop_value(vm);
if (m.type != VAL_MAP) { fprintf(stderr, "KEYS expects map\n"); exit(1); } if (m.type != VAL_MAP) {
fprintf(stderr, "KEYS expects map\n");
exit(1);
}
Value arr = map_keys_array(&m); Value arr = map_keys_array(&m);
free_value(m); free_value(m);
push_value(vm, arr); push_value(vm, arr);

View file

@ -33,17 +33,23 @@
* @date 2025-10-16 * @date 2025-10-16
*/ */
case OP_MAKE_MAP: { case OP_MAKE_MAP: {
int pairs = inst.operand; int pairs = inst.operand;
if (pairs < 0) { fprintf(stderr, "MAKE_MAP invalid pair count\n"); exit(1); } if (pairs < 0) {
fprintf(stderr, "MAKE_MAP invalid pair count\n");
exit(1);
}
Value m = make_map_empty(); Value m = make_map_empty();
for (int i = 0; i < pairs; ++i) { for (int i = 0; i < pairs; ++i) {
Value val = pop_value(vm); Value val = pop_value(vm);
Value key = pop_value(vm); Value key = pop_value(vm);
if (key.type != VAL_STRING) { fprintf(stderr, "Map literal keys must be strings\n"); exit(1); } if (key.type != VAL_STRING) {
fprintf(stderr, "Map literal keys must be strings\n");
exit(1);
}
if (!map_set(&m, key.s ? key.s : "", val)) { if (!map_set(&m, key.s ? key.s : "", val)) {
fprintf(stderr, "Map literal set failed\n"); exit(1); fprintf(stderr, "Map literal set failed\n");
exit(1);
} }
free_value(key); free_value(key);
} }

View file

@ -33,7 +33,10 @@
case OP_VALUES: { case OP_VALUES: {
Value m = pop_value(vm); Value m = pop_value(vm);
if (m.type != VAL_MAP) { fprintf(stderr, "VALUES expects map\n"); exit(1); } if (m.type != VAL_MAP) {
fprintf(stderr, "VALUES expects map\n");
exit(1);
}
Value arr = map_values_array(&m); Value arr = map_values_array(&m);
free_value(m); free_value(m);
push_value(vm, arr); push_value(vm, arr);

View file

@ -27,7 +27,10 @@
case OP_ABS: { case OP_ABS: {
Value x = pop_value(vm); Value x = pop_value(vm);
if (x.type != VAL_INT) { fprintf(stderr, "ABS expects int\n"); exit(1); } if (x.type != VAL_INT) {
fprintf(stderr, "ABS expects int\n");
exit(1);
}
int64_t v = x.i; int64_t v = x.i;
if (v < 0) v = -v; if (v < 0) v = -v;
push_value(vm, make_int(v)); push_value(vm, make_int(v));

View file

@ -31,11 +31,15 @@ case OP_FMAX: {
Value out; Value out;
if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) { if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r; int64_t ii = (int64_t)r;
if ((double)ii == r) out = make_int(ii); else out = make_float(r); if ((double)ii == r)
out = make_int(ii);
else
out = make_float(r);
} else { } else {
out = make_float(r); out = make_float(r);
} }
free_value(a); free_value(b); free_value(a);
free_value(b);
push_value(vm, out); push_value(vm, out);
break; break;
} }

View file

@ -31,11 +31,15 @@ case OP_FMIN: {
Value out; Value out;
if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) { if (!isnan(r) && !isinf(r) && r >= (double)INT64_MIN && r <= (double)INT64_MAX) {
int64_t ii = (int64_t)r; int64_t ii = (int64_t)r;
if ((double)ii == r) out = make_int(ii); else out = make_float(r); if ((double)ii == r)
out = make_int(ii);
else
out = make_float(r);
} else { } else {
out = make_float(r); out = make_float(r);
} }
free_value(a); free_value(b); free_value(a);
free_value(b);
push_value(vm, out); push_value(vm, out);
break; break;
} }

View file

@ -25,8 +25,14 @@ case OP_GCD: {
} }
int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d; int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d;
int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d; int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d;
if (a == INT64_MIN) a = (int64_t)INT64_MAX; else if (a < 0) a = -a; if (a == INT64_MIN)
if (b == INT64_MIN) b = (int64_t)INT64_MAX; else if (b < 0) b = -b; a = (int64_t)INT64_MAX;
else if (a < 0)
a = -a;
if (b == INT64_MIN)
b = (int64_t)INT64_MAX;
else if (b < 0)
b = -b;
while (b != 0) { while (b != 0) {
int64_t t = a % b; int64_t t = a % b;
a = b; a = b;

View file

@ -30,7 +30,8 @@ case OP_ISQRT: {
uint64_t n = (uint64_t)a; uint64_t n = (uint64_t)a;
uint64_t x = 0; uint64_t x = 0;
uint64_t bit = (uint64_t)1 << 62; /* highest even bit set */ uint64_t bit = (uint64_t)1 << 62; /* highest even bit set */
while (bit > n) bit >>= 2; while (bit > n)
bit >>= 2;
while (bit != 0) { while (bit != 0) {
if (n >= x + bit) { if (n >= x + bit) {
n -= x + bit; n -= x + bit;

View file

@ -25,17 +25,26 @@ case OP_LCM: {
} }
int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d; int64_t a = (va.type == VAL_INT) ? va.i : (int64_t)va.d;
int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d; int64_t b = (vb.type == VAL_INT) ? vb.i : (int64_t)vb.d;
if (a == INT64_MIN) a = (int64_t)INT64_MAX; else if (a < 0) a = -a; if (a == INT64_MIN)
if (b == INT64_MIN) b = (int64_t)INT64_MAX; else if (b < 0) b = -b; a = (int64_t)INT64_MAX;
else if (a < 0)
a = -a;
if (b == INT64_MIN)
b = (int64_t)INT64_MAX;
else if (b < 0)
b = -b;
if (a == 0 || b == 0) { if (a == 0 || b == 0) {
free_value(va); free_value(vb); free_value(va);
free_value(vb);
push_value(vm, make_int(0)); push_value(vm, make_int(0));
break; break;
} }
/* gcd(a,b) */ /* gcd(a,b) */
int64_t x = a, y = b; int64_t x = a, y = b;
while (y != 0) { while (y != 0) {
int64_t t = x % y; x = y; y = t; int64_t t = x % y;
x = y;
y = t;
} }
int64_t g = x; int64_t g = x;
/* lcm = (a/g)*b (attempt to reduce overflow) */ /* lcm = (a/g)*b (attempt to reduce overflow) */

View file

@ -34,8 +34,12 @@
case OP_MAX: { case OP_MAX: {
Value b = pop_value(vm); Value b = pop_value(vm);
Value a = pop_value(vm); Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) { fprintf(stderr, "MAX expects ints\n"); exit(1); } if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "MAX expects ints\n");
exit(1);
}
push_value(vm, make_int(a.i > b.i ? a.i : b.i)); push_value(vm, make_int(a.i > b.i ? a.i : b.i));
free_value(a); free_value(b); free_value(a);
free_value(b);
break; break;
} }

View file

@ -34,8 +34,12 @@
case OP_MIN: { case OP_MIN: {
Value b = pop_value(vm); Value b = pop_value(vm);
Value a = pop_value(vm); Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) { fprintf(stderr, "MIN expects ints\n"); exit(1); } if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "MIN expects ints\n");
exit(1);
}
push_value(vm, make_int(a.i < b.i ? a.i : b.i)); push_value(vm, make_int(a.i < b.i ? a.i : b.i));
free_value(a); free_value(b); free_value(a);
free_value(b);
break; break;
} }

View file

@ -34,11 +34,16 @@
case OP_POW: { case OP_POW: {
Value b = pop_value(vm); Value b = pop_value(vm);
Value a = pop_value(vm); Value a = pop_value(vm);
if (a.type != VAL_INT || b.type != VAL_INT) { fprintf(stderr, "POW expects ints\n"); exit(1); } if (a.type != VAL_INT || b.type != VAL_INT) {
fprintf(stderr, "POW expects ints\n");
exit(1);
}
int64_t base = a.i; int64_t base = a.i;
int64_t exp = b.i; int64_t exp = b.i;
int64_t res = 1; int64_t res = 1;
if (exp < 0) { res = 0; } else { if (exp < 0) {
res = 0;
} else {
while (exp > 0) { while (exp > 0) {
if (exp & 1) res *= base; if (exp & 1) res *= base;
base *= base; base *= base;
@ -46,6 +51,7 @@ case OP_POW: {
} }
} }
push_value(vm, make_int(res)); push_value(vm, make_int(res));
free_value(a); free_value(b); free_value(a);
free_value(b);
break; break;
} }

View file

@ -36,12 +36,21 @@
case OP_RANDOM_INT: { case OP_RANDOM_INT: {
Value hi = pop_value(vm); Value hi = pop_value(vm);
Value lo = pop_value(vm); Value lo = pop_value(vm);
if (lo.type != VAL_INT || hi.type != VAL_INT) { fprintf(stderr, "RANDOM_INT expects (int, int)\n"); exit(1); } if (lo.type != VAL_INT || hi.type != VAL_INT) {
fprintf(stderr, "RANDOM_INT expects (int, int)\n");
exit(1);
}
int64_t a = lo.i, b = hi.i; int64_t a = lo.i, b = hi.i;
if (b <= a) { push_value(vm, make_int((int64_t)a)); free_value(lo); free_value(hi); break; } if (b <= a) {
push_value(vm, make_int((int64_t)a));
free_value(lo);
free_value(hi);
break;
}
int64_t span = b - a; int64_t span = b - a;
int64_t r = (int64_t)(rand() % (span)); int64_t r = (int64_t)(rand() % (span));
push_value(vm, make_int(a + r)); push_value(vm, make_int(a + r));
free_value(lo); free_value(hi); free_value(lo);
free_value(hi);
break; break;
} }

View file

@ -32,7 +32,10 @@
case OP_RANDOM_SEED: { case OP_RANDOM_SEED: {
Value seed = pop_value(vm); Value seed = pop_value(vm);
if (seed.type != VAL_INT) { fprintf(stderr, "RANDOM_SEED expects int\n"); exit(1); } if (seed.type != VAL_INT) {
fprintf(stderr, "RANDOM_SEED expects int\n");
exit(1);
}
srand((unsigned int)seed.i); srand((unsigned int)seed.i);
free_value(seed); free_value(seed);
push_value(vm, make_int(0)); push_value(vm, make_int(0));

View file

@ -20,9 +20,12 @@ case OP_SIGN: {
if (v.type == VAL_INT) { if (v.type == VAL_INT) {
out = (v.i > 0) - (v.i < 0); out = (v.i > 0) - (v.i < 0);
} else if (v.type == VAL_FLOAT) { } else if (v.type == VAL_FLOAT) {
if (v.d > 0.0) out = 1; if (v.d > 0.0)
else if (v.d < 0.0) out = -1; out = 1;
else out = 0; else if (v.d < 0.0)
out = -1;
else
out = 0;
} else { } else {
fprintf(stderr, "Runtime type error: SIGN expects number, got %s\n", value_type_name(v.type)); fprintf(stderr, "Runtime type error: SIGN expects number, got %s\n", value_type_name(v.type));
exit(1); exit(1);

View file

@ -20,7 +20,8 @@ case OP_NC_CLEAR: {
push_value(vm, make_int(-1)); push_value(vm, make_int(-1));
} }
#else #else
(void)_fun_nc; (void)_fun_nc_std; (void)_fun_nc;
(void)_fun_nc_std;
push_value(vm, make_int(-1)); push_value(vm, make_int(-1));
#endif #endif
break; break;

View file

@ -31,7 +31,8 @@ case OP_NC_DRAW_TEXT: {
push_value(vm, make_int(-1)); push_value(vm, make_int(-1));
} }
#else #else
(void)_fun_nc; (void)_fun_nc_std; (void)_fun_nc;
(void)_fun_nc_std;
if (text) free(text); if (text) free(text);
push_value(vm, make_int(-1)); push_value(vm, make_int(-1));
#endif #endif

View file

@ -36,7 +36,9 @@ case OP_NC_GETCH: {
push_value(vm, make_int(-1)); push_value(vm, make_int(-1));
} }
#else #else
(void)_fun_nc; (void)_fun_nc_std; (void)timeout_ms; (void)_fun_nc;
(void)_fun_nc_std;
(void)timeout_ms;
push_value(vm, make_int(-1)); push_value(vm, make_int(-1));
#endif #endif
break; break;

View file

@ -12,14 +12,21 @@
/* NC_INIT */ /* NC_INIT */
case OP_NC_INIT: { case OP_NC_INIT: {
#ifdef FUN_WITH_NOTCURSES #ifdef FUN_WITH_NOTCURSES
if (_fun_nc) { push_value(vm, make_int(1)); break; } if (_fun_nc) {
push_value(vm, make_int(1));
break;
}
struct notcurses_options opts = {0}; struct notcurses_options opts = {0};
_fun_nc = notcurses_core_init(&opts, NULL); _fun_nc = notcurses_core_init(&opts, NULL);
if (!_fun_nc) { push_value(vm, make_int(0)); break; } if (!_fun_nc) {
push_value(vm, make_int(0));
break;
}
_fun_nc_std = notcurses_stdplane(_fun_nc); _fun_nc_std = notcurses_stdplane(_fun_nc);
push_value(vm, make_int(1)); push_value(vm, make_int(1));
#else #else
(void)_fun_nc; (void)_fun_nc_std; (void)_fun_nc;
(void)_fun_nc_std;
fprintf(stderr, "Notcurses support disabled at build time. Reconfigure with -DFUN_WITH_NOTCURSES=ON.\n"); fprintf(stderr, "Notcurses support disabled at build time. Reconfigure with -DFUN_WITH_NOTCURSES=ON.\n");
push_value(vm, make_int(0)); push_value(vm, make_int(0));
#endif #endif

View file

@ -18,7 +18,8 @@ case OP_NC_SHUTDOWN: {
_fun_nc_std = NULL; _fun_nc_std = NULL;
} }
#else #else
(void)_fun_nc; (void)_fun_nc_std; (void)_fun_nc;
(void)_fun_nc_std;
#endif #endif
push_value(vm, make_int(0)); push_value(vm, make_int(0));
break; break;

View file

@ -16,10 +16,16 @@ case OP_OPENSSL_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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_openssl_md5_hex((const unsigned char *)s, strlen(s)); char *hex = fun_openssl_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_OPENSSL_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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_openssl_ripemd160_hex((const unsigned char *)s, strlen(s)); char *hex = fun_openssl_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_OPENSSL_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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_openssl_sha256_hex((const unsigned char *)s, strlen(s)); char *hex = fun_openssl_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_OPENSSL_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) {
push_value(vm, make_string(""));
break;
}
char *hex = fun_openssl_sha512_hex((const unsigned char *)s, strlen(s)); char *hex = fun_openssl_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);

View file

@ -17,8 +17,8 @@
* Stack after: [int ms] * Stack after: [int ms]
*/ */
#include <time.h>
#include <stdint.h> #include <stdint.h>
#include <time.h>
case OP_CLOCK_MONO_MS: { case OP_CLOCK_MONO_MS: {
int64_t ms; int64_t ms;

View file

@ -21,9 +21,9 @@
* - If types are wrong, prints error and pushes empty string to keep stack safe. * - If types are wrong, prints error and pushes empty string to keep stack safe.
*/ */
#include <time.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <time.h>
case OP_DATE_FORMAT: { case OP_DATE_FORMAT: {
Value fmt = pop_value(vm); Value fmt = pop_value(vm);
@ -42,7 +42,8 @@ case OP_DATE_FORMAT: {
#else #else
struct tm *ptm = localtime(&secs); struct tm *ptm = localtime(&secs);
if (!ptm) { if (!ptm) {
free_value(ms); free_value(fmt); free_value(ms);
free_value(fmt);
push_value(vm, make_string("")); push_value(vm, make_string(""));
break; break;
} }

View file

@ -69,8 +69,10 @@ case OP_PROC_RUN: {
out[len] = '\0'; out[len] = '\0';
int status = pclose(fp); int status = pclose(fp);
#ifdef __unix__ #ifdef __unix__
if (WIFEXITED(status)) exit_code = WEXITSTATUS(status); if (WIFEXITED(status))
else exit_code = -1; exit_code = WEXITSTATUS(status);
else
exit_code = -1;
#else #else
exit_code = status; exit_code = status;
#endif #endif

View file

@ -21,9 +21,12 @@ case OP_PROC_SYSTEM: {
int status = system(cmd); int status = system(cmd);
int code = -1; int code = -1;
#ifdef __unix__ #ifdef __unix__
if (status == -1) code = -1; if (status == -1)
else if (WIFEXITED(status)) code = WEXITSTATUS(status); code = -1;
else code = -1; else if (WIFEXITED(status))
code = WEXITSTATUS(status);
else
code = -1;
#else #else
code = status; code = status;
#endif #endif

View file

@ -22,8 +22,8 @@
/* Platform-specific headers guarded per OS to avoid leaking problematic macros */ /* Platform-specific headers guarded per OS to avoid leaking problematic macros */
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#include <bcrypt.h> #include <bcrypt.h>
#include <windows.h>
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
#include <stdlib.h> #include <stdlib.h>
#include <sys/random.h> #include <sys/random.h>
@ -105,8 +105,10 @@ case OP_RANDOM_NUMBER: {
if (!ok) { if (!ok) {
int fd = open("/dev/urandom", O_RDONLY); int fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) { if (fd >= 0) {
size_t off = 0; ssize_t n; size_t off = 0;
while (off < (size_t)len && (n = read(fd, raw + off, (size_t)len - off)) > 0) off += (size_t)n; ssize_t n;
while (off < (size_t)len && (n = read(fd, raw + off, (size_t)len - off)) > 0)
off += (size_t)n;
close(fd); close(fd);
ok = (off == (size_t)len); ok = (off == (size_t)len);
} }

View file

@ -37,10 +37,19 @@ case OP_SERIAL_CONFIG: {
// Data bits // Data bits
options.c_cflag &= ~CSIZE; options.c_cflag &= ~CSIZE;
switch (data_bits) { switch (data_bits) {
case 5: options.c_cflag |= CS5; break; case 5:
case 6: options.c_cflag |= CS6; break; options.c_cflag |= CS5;
case 7: options.c_cflag |= CS7; break; break;
case 8: default: options.c_cflag |= CS8; break; case 6:
options.c_cflag |= CS6;
break;
case 7:
options.c_cflag |= CS7;
break;
case 8:
default:
options.c_cflag |= CS8;
break;
} }
// Parity // Parity

View file

@ -94,25 +94,63 @@ case OP_SERIAL_OPEN: {
speed_t speed; speed_t speed;
switch (baud) { switch (baud) {
case 50: speed = B50; break; case 50:
case 75: speed = B75; break; speed = B50;
case 110: speed = B110; break; break;
case 134: speed = B134; break; case 75:
case 150: speed = B150; break; speed = B75;
case 200: speed = B200; break; break;
case 300: speed = B300; break; case 110:
case 600: speed = B600; break; speed = B110;
case 1200: speed = B1200; break; break;
case 1800: speed = B1800; break; case 134:
case 2400: speed = B2400; break; speed = B134;
case 4800: speed = B4800; break; break;
case 9600: speed = B9600; break; case 150:
case 19200: speed = B19200; break; speed = B150;
case 38400: speed = B38400; break; break;
case 57600: speed = B57600; break; case 200:
case 115200: speed = B115200; break; speed = B200;
case 230400: speed = B230400; break; break;
default: speed = B9600; break; case 300:
speed = B300;
break;
case 600:
speed = B600;
break;
case 1200:
speed = B1200;
break;
case 1800:
speed = B1800;
break;
case 2400:
speed = B2400;
break;
case 4800:
speed = B4800;
break;
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
case 57600:
speed = B57600;
break;
case 115200:
speed = B115200;
break;
case 230400:
speed = B230400;
break;
default:
speed = B9600;
break;
} }
fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY); fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);

View file

@ -30,7 +30,8 @@ case OP_SOCK_RECV: {
if (out) { if (out) {
ssize_t n = recv(fd, out, (size_t)maxlen, 0); ssize_t n = recv(fd, out, (size_t)maxlen, 0);
if (n <= 0) { if (n <= 0) {
free(out); out = NULL; free(out);
out = NULL;
} else { } else {
out[n] = '\0'; out[n] = '\0';
} }

View file

@ -26,7 +26,10 @@ case OP_SOCK_SEND: {
const char *buf = datav.s ? datav.s : ""; const char *buf = datav.s ? datav.s : "";
size_t len = strlen(buf); size_t len = strlen(buf);
ssize_t n = send(fd, buf, len, 0); ssize_t n = send(fd, buf, len, 0);
if (n >= 0) sent = (int)n; else sent = -1; if (n >= 0)
sent = (int)n;
else
sent = -1;
#endif #endif
free_value(datav); free_value(datav);
free_value(fdv); free_value(fdv);

View file

@ -35,7 +35,10 @@ case OP_SOCK_TCP_CONNECT: {
for (rp = res; rp != NULL; rp = rp->ai_next) { for (rp = res; rp != NULL; rp = rp->ai_next) {
int s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); int s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (s < 0) continue; if (s < 0) continue;
if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) { fd = s; break; } if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) {
fd = s;
break;
}
close(s); close(s);
} }
if (res) freeaddrinfo(res); if (res) freeaddrinfo(res);

View file

@ -11,8 +11,8 @@
#include <string.h> #include <string.h>
#ifdef __unix__ #ifdef __unix__
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#endif #endif

View file

@ -60,12 +60,21 @@ static void fun_thr_lock_init(void) {
g_thr_lock_inited = 1; g_thr_lock_inited = 1;
} }
} }
static void fun_lock(void) { if (!g_thr_lock_inited) fun_thr_lock_init(); EnterCriticalSection(&g_thr_lock); } static void fun_lock(void) {
static void fun_unlock(void) { LeaveCriticalSection(&g_thr_lock); } if (!g_thr_lock_inited) fun_thr_lock_init();
EnterCriticalSection(&g_thr_lock);
}
static void fun_unlock(void) {
LeaveCriticalSection(&g_thr_lock);
}
#else #else
static pthread_mutex_t g_thr_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t g_thr_lock = PTHREAD_MUTEX_INITIALIZER;
static void fun_lock(void) { pthread_mutex_lock(&g_thr_lock); } static void fun_lock(void) {
static void fun_unlock(void) { pthread_mutex_unlock(&g_thr_lock); } pthread_mutex_lock(&g_thr_lock);
}
static void fun_unlock(void) {
pthread_mutex_unlock(&g_thr_lock);
}
#endif #endif
typedef struct { typedef struct {
@ -130,7 +139,13 @@ static int fun_alloc_thread_slot(void) {
fun_lock(); fun_lock();
int idx = -1; int idx = -1;
for (int i = 0; i < FUN_MAX_THREADS; ++i) { for (int i = 0; i < FUN_MAX_THREADS; ++i) {
if (!g_threads[i].used) { g_threads[i].used = 1; g_threads[i].done = 0; g_threads[i].result = make_nil(); idx = i; break; } if (!g_threads[i].used) {
g_threads[i].used = 1;
g_threads[i].done = 0;
g_threads[i].result = make_nil();
idx = i;
break;
}
} }
fun_unlock(); fun_unlock();
return idx; return idx;
@ -176,16 +191,20 @@ static int fun_thread_spawn(Value fnVal, Value argsMaybe, int hasArgs) {
if (slot < 0) { if (slot < 0) {
fprintf(stderr, "Runtime error: too many threads\n"); fprintf(stderr, "Runtime error: too many threads\n");
/* free args */ /* free args */
for (int i = 0; i < argc; ++i) free_value(args[i]); for (int i = 0; i < argc; ++i)
free_value(args[i]);
free(args); free(args);
return 0; return 0;
} }
FunTask *task = (FunTask *)calloc(1, sizeof(FunTask)); FunTask *task = (FunTask *)calloc(1, sizeof(FunTask));
if (!task) { if (!task) {
for (int i = 0; i < argc; ++i) free_value(args[i]); for (int i = 0; i < argc; ++i)
free_value(args[i]);
free(args); free(args);
fun_lock(); g_threads[slot].used = 0; fun_unlock(); fun_lock();
g_threads[slot].used = 0;
fun_unlock();
return 0; return 0;
} }
task->fn = fnVal.fn; task->fn = fnVal.fn;
@ -197,25 +216,35 @@ static int fun_thread_spawn(Value fnVal, Value argsMaybe, int hasArgs) {
HANDLE h = CreateThread(NULL, 0, fun_thread_main, (LPVOID)task, 0, &g_threads[slot].threadId); HANDLE h = CreateThread(NULL, 0, fun_thread_main, (LPVOID)task, 0, &g_threads[slot].threadId);
if (!h) { if (!h) {
fprintf(stderr, "Runtime error: CreateThread failed\n"); fprintf(stderr, "Runtime error: CreateThread failed\n");
for (int i = 0; i < argc; ++i) free_value(args[i]); for (int i = 0; i < argc; ++i)
free_value(args[i]);
free(args); free(args);
free(task); free(task);
fun_lock(); g_threads[slot].used = 0; fun_unlock(); fun_lock();
g_threads[slot].used = 0;
fun_unlock();
return 0; return 0;
} }
fun_lock(); g_threads[slot].handle = h; fun_unlock(); fun_lock();
g_threads[slot].handle = h;
fun_unlock();
#else #else
pthread_t tid; pthread_t tid;
int rc = pthread_create(&tid, NULL, fun_thread_main, (void *)task); int rc = pthread_create(&tid, NULL, fun_thread_main, (void *)task);
if (rc != 0) { if (rc != 0) {
fprintf(stderr, "Runtime error: pthread_create failed\n"); fprintf(stderr, "Runtime error: pthread_create failed\n");
for (int i = 0; i < argc; ++i) free_value(args[i]); for (int i = 0; i < argc; ++i)
free_value(args[i]);
free(args); free(args);
free(task); free(task);
fun_lock(); g_threads[slot].used = 0; fun_unlock(); fun_lock();
g_threads[slot].used = 0;
fun_unlock();
return 0; return 0;
} }
fun_lock(); g_threads[slot].handle = tid; fun_unlock(); fun_lock();
g_threads[slot].handle = tid;
fun_unlock();
#endif #endif
return slot + 1; /* external thread id: 1..N */ return slot + 1; /* external thread id: 1..N */

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