Fixed the code style in *.c files to two spaces indentation and add a linter named funstx to Fun. (0.39.0)
This commit is contained in:
parent
33001a7ad2
commit
03b2532474
237 changed files with 17550 additions and 13911 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include <encoding/base64.fun>
|
#include <encoding/base64.fun>
|
||||||
|
|
||||||
print("=== Base64 demo ===")
|
print("=== Base64 demo ===")
|
||||||
|
|
||||||
// Bytes for the ASCII string "Hello"
|
// Bytes for the ASCII string "Hello"
|
||||||
bytes = [0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
bytes = [0x48, 0x65, 0x6c, 0x6c, 0x6f]
|
||||||
|
|
|
||||||
507
src/bytecode.c
507
src/bytecode.c
|
|
@ -8,11 +8,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bytecode.h"
|
#include "bytecode.h"
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
Bytecode *bytecode_new(void) {
|
Bytecode *bytecode_new(void) {
|
||||||
Bytecode *bc = (Bytecode*)malloc(sizeof(Bytecode));
|
Bytecode *bc = (Bytecode *)malloc(sizeof(Bytecode));
|
||||||
bc->instructions = NULL;
|
bc->instructions = NULL;
|
||||||
bc->instr_count = 0;
|
bc->instr_count = 0;
|
||||||
bc->constants = NULL;
|
bc->constants = NULL;
|
||||||
|
|
@ -23,13 +23,13 @@ Bytecode *bytecode_new(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytecode_add_constant(Bytecode *bc, Value v) {
|
int bytecode_add_constant(Bytecode *bc, Value v) {
|
||||||
bc->constants = (Value*)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1));
|
bc->constants = (Value *)realloc(bc->constants, sizeof(Value) * (bc->const_count + 1));
|
||||||
bc->constants[bc->const_count] = copy_value(&v);
|
bc->constants[bc->const_count] = copy_value(&v);
|
||||||
return bc->const_count++;
|
return bc->const_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytecode_add_instruction(Bytecode *bc, OpCode op, int32_t operand) {
|
int bytecode_add_instruction(Bytecode *bc, OpCode op, int32_t operand) {
|
||||||
bc->instructions = (Instruction*)realloc(bc->instructions, sizeof(Instruction) * (bc->instr_count + 1));
|
bc->instructions = (Instruction *)realloc(bc->instructions, sizeof(Instruction) * (bc->instr_count + 1));
|
||||||
bc->instructions[bc->instr_count].op = op;
|
bc->instructions[bc->instr_count].op = op;
|
||||||
bc->instructions[bc->instr_count].operand = operand;
|
bc->instructions[bc->instr_count].operand = operand;
|
||||||
return bc->instr_count++;
|
return bc->instr_count++;
|
||||||
|
|
@ -48,178 +48,343 @@ void bytecode_free(Bytecode *bc) {
|
||||||
}
|
}
|
||||||
free(bc->constants);
|
free(bc->constants);
|
||||||
free(bc->instructions);
|
free(bc->instructions);
|
||||||
if (bc->name) free((void*)bc->name);
|
if (bc->name) free((void *)bc->name);
|
||||||
if (bc->source_file) free((void*)bc->source_file);
|
if (bc->source_file) free((void *)bc->source_file);
|
||||||
free(bc);
|
free(bc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *opcode_name(OpCode op) {
|
static const char *opcode_name(OpCode op) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case OP_NOP: return "NOP";
|
case OP_NOP:
|
||||||
case OP_LOAD_CONST: return "LOAD_CONST";
|
return "NOP";
|
||||||
case OP_LOAD_LOCAL: return "LOAD_LOCAL";
|
case OP_LOAD_CONST:
|
||||||
case OP_STORE_LOCAL: return "STORE_LOCAL";
|
return "LOAD_CONST";
|
||||||
case OP_LOAD_GLOBAL: return "LOAD_GLOBAL";
|
case OP_LOAD_LOCAL:
|
||||||
case OP_STORE_GLOBAL: return "STORE_GLOBAL";
|
return "LOAD_LOCAL";
|
||||||
case OP_ADD: return "ADD";
|
case OP_STORE_LOCAL:
|
||||||
case OP_SUB: return "SUB";
|
return "STORE_LOCAL";
|
||||||
case OP_MUL: return "MUL";
|
case OP_LOAD_GLOBAL:
|
||||||
case OP_DIV: return "DIV";
|
return "LOAD_GLOBAL";
|
||||||
case OP_LT: return "LT";
|
case OP_STORE_GLOBAL:
|
||||||
case OP_LTE: return "LTE";
|
return "STORE_GLOBAL";
|
||||||
case OP_GT: return "GT";
|
case OP_ADD:
|
||||||
case OP_GTE: return "GTE";
|
return "ADD";
|
||||||
case OP_EQ: return "EQ";
|
case OP_SUB:
|
||||||
case OP_NEQ: return "NEQ";
|
return "SUB";
|
||||||
case OP_POP: return "POP";
|
case OP_MUL:
|
||||||
case OP_JUMP: return "JUMP";
|
return "MUL";
|
||||||
case OP_JUMP_IF_FALSE: return "JUMP_IF_FALSE";
|
case OP_DIV:
|
||||||
case OP_CALL: return "CALL";
|
return "DIV";
|
||||||
case OP_RETURN: return "RETURN";
|
case OP_LT:
|
||||||
case OP_PRINT: return "PRINT";
|
return "LT";
|
||||||
case OP_ECHO: return "ECHO";
|
case OP_LTE:
|
||||||
case OP_HALT: return "HALT";
|
return "LTE";
|
||||||
case OP_MOD: return "MOD";
|
case OP_GT:
|
||||||
case OP_AND: return "AND";
|
return "GT";
|
||||||
case OP_OR: return "OR";
|
case OP_GTE:
|
||||||
case OP_NOT: return "NOT";
|
return "GTE";
|
||||||
case OP_DUP: return "DUP";
|
case OP_EQ:
|
||||||
case OP_SWAP: return "SWAP";
|
return "EQ";
|
||||||
case OP_MAKE_ARRAY: return "MAKE_ARRAY";
|
case OP_NEQ:
|
||||||
case OP_INDEX_GET: return "INDEX_GET";
|
return "NEQ";
|
||||||
case OP_INDEX_SET: return "INDEX_SET";
|
case OP_POP:
|
||||||
case OP_LEN: return "LEN";
|
return "POP";
|
||||||
case OP_PUSH: return "ARR_PUSH";
|
case OP_JUMP:
|
||||||
case OP_APOP: return "ARR_POP";
|
return "JUMP";
|
||||||
case OP_SET: return "ARR_SET";
|
case OP_JUMP_IF_FALSE:
|
||||||
case OP_INSERT: return "ARR_INSERT";
|
return "JUMP_IF_FALSE";
|
||||||
case OP_REMOVE: return "ARR_REMOVE";
|
case OP_CALL:
|
||||||
case OP_SLICE: return "SLICE";
|
return "CALL";
|
||||||
case OP_TO_NUMBER: return "TO_NUMBER";
|
case OP_RETURN:
|
||||||
case OP_TO_STRING: return "TO_STRING";
|
return "RETURN";
|
||||||
case OP_TYPEOF: return "TYPEOF";
|
case OP_PRINT:
|
||||||
case OP_CAST: return "CAST";
|
return "PRINT";
|
||||||
case OP_SPLIT: return "SPLIT";
|
case OP_ECHO:
|
||||||
case OP_JOIN: return "JOIN";
|
return "ECHO";
|
||||||
case OP_SUBSTR: return "SUBSTR";
|
case OP_HALT:
|
||||||
case OP_FIND: return "FIND";
|
return "HALT";
|
||||||
case OP_REGEX_MATCH: return "REGEX_MATCH";
|
case OP_MOD:
|
||||||
case OP_REGEX_SEARCH: return "REGEX_SEARCH";
|
return "MOD";
|
||||||
case OP_REGEX_REPLACE: return "REGEX_REPLACE";
|
case OP_AND:
|
||||||
case OP_CONTAINS: return "CONTAINS";
|
return "AND";
|
||||||
case OP_INDEX_OF: return "INDEX_OF";
|
case OP_OR:
|
||||||
case OP_CLEAR: return "CLEAR";
|
return "OR";
|
||||||
case OP_ENUMERATE: return "ENUMERATE";
|
case OP_NOT:
|
||||||
case OP_ZIP: return "ZIP";
|
return "NOT";
|
||||||
case OP_MIN: return "MIN";
|
case OP_DUP:
|
||||||
case OP_MAX: return "MAX";
|
return "DUP";
|
||||||
case OP_CLAMP: return "CLAMP";
|
case OP_SWAP:
|
||||||
case OP_ABS: return "ABS";
|
return "SWAP";
|
||||||
case OP_POW: return "POW";
|
case OP_MAKE_ARRAY:
|
||||||
case OP_RANDOM_SEED: return "RANDOM_SEED";
|
return "MAKE_ARRAY";
|
||||||
case OP_RANDOM_INT: return "RANDOM_INT";
|
case OP_INDEX_GET:
|
||||||
case OP_MAKE_MAP: return "MAKE_MAP";
|
return "INDEX_GET";
|
||||||
case OP_KEYS: return "KEYS";
|
case OP_INDEX_SET:
|
||||||
case OP_VALUES: return "VALUES";
|
return "INDEX_SET";
|
||||||
case OP_HAS_KEY: return "HAS_KEY";
|
case OP_LEN:
|
||||||
case OP_READ_FILE: return "READ_FILE";
|
return "LEN";
|
||||||
case OP_WRITE_FILE: return "WRITE_FILE";
|
case OP_PUSH:
|
||||||
case OP_ENV: return "ENV";
|
return "ARR_PUSH";
|
||||||
case OP_INPUT_LINE: return "INPUT_LINE";
|
case OP_APOP:
|
||||||
case OP_PROC_RUN: return "PROC_RUN";
|
return "ARR_POP";
|
||||||
case OP_PROC_SYSTEM: return "PROC_SYSTEM";
|
case OP_SET:
|
||||||
case OP_TIME_NOW_MS: return "TIME_NOW_MS";
|
return "ARR_SET";
|
||||||
case OP_CLOCK_MONO_MS: return "CLOCK_MONO_MS";
|
case OP_INSERT:
|
||||||
case OP_DATE_FORMAT: return "DATE_FORMAT";
|
return "ARR_INSERT";
|
||||||
case OP_ENV_ALL: return "ENV_ALL";
|
case OP_REMOVE:
|
||||||
case OP_FUN_VERSION: return "FUN_VERSION";
|
return "ARR_REMOVE";
|
||||||
case OP_THREAD_SPAWN: return "THREAD_SPAWN";
|
case OP_SLICE:
|
||||||
case OP_THREAD_JOIN: return "THREAD_JOIN";
|
return "SLICE";
|
||||||
case OP_SLEEP_MS: return "SLEEP_MS";
|
case OP_TO_NUMBER:
|
||||||
case OP_RANDOM_NUMBER: return "RANDOM_NUMBER";
|
return "TO_NUMBER";
|
||||||
case OP_BAND: return "BAND";
|
case OP_TO_STRING:
|
||||||
case OP_BOR: return "BOR";
|
return "TO_STRING";
|
||||||
case OP_BXOR: return "BXOR";
|
case OP_TYPEOF:
|
||||||
case OP_BNOT: return "BNOT";
|
return "TYPEOF";
|
||||||
case OP_SHL: return "SHL";
|
case OP_CAST:
|
||||||
case OP_SHR: return "SHR";
|
return "CAST";
|
||||||
case OP_ROTL: return "ROTL";
|
case OP_SPLIT:
|
||||||
case OP_ROTR: return "ROTR";
|
return "SPLIT";
|
||||||
case OP_JSON_PARSE: return "JSON_PARSE";
|
case OP_JOIN:
|
||||||
case OP_JSON_STRINGIFY: return "JSON_STRINGIFY";
|
return "JOIN";
|
||||||
case OP_JSON_FROM_FILE: return "JSON_FROM_FILE";
|
case OP_SUBSTR:
|
||||||
case OP_JSON_TO_FILE: return "JSON_TO_FILE";
|
return "SUBSTR";
|
||||||
case OP_CURL_GET: return "CURL_GET";
|
case OP_FIND:
|
||||||
case OP_CURL_POST: return "CURL_POST";
|
return "FIND";
|
||||||
case OP_CURL_DOWNLOAD: return "CURL_DOWNLOAD";
|
case OP_REGEX_MATCH:
|
||||||
case OP_SQLITE_OPEN: return "SQLITE_OPEN";
|
return "REGEX_MATCH";
|
||||||
case OP_SQLITE_CLOSE: return "SQLITE_CLOSE";
|
case OP_REGEX_SEARCH:
|
||||||
case OP_SQLITE_EXEC: return "SQLITE_EXEC";
|
return "REGEX_SEARCH";
|
||||||
case OP_SQLITE_QUERY: return "SQLITE_QUERY";
|
case OP_REGEX_REPLACE:
|
||||||
case OP_LIBSQL_OPEN: return "LIBSQL_OPEN";
|
return "REGEX_REPLACE";
|
||||||
case OP_LIBSQL_CLOSE: return "LIBSQL_CLOSE";
|
case OP_CONTAINS:
|
||||||
case OP_LIBSQL_EXEC: return "LIBSQL_EXEC";
|
return "CONTAINS";
|
||||||
case OP_LIBSQL_QUERY: return "LIBSQL_QUERY";
|
case OP_INDEX_OF:
|
||||||
case OP_PCSC_ESTABLISH: return "PCSC_ESTABLISH";
|
return "INDEX_OF";
|
||||||
case OP_PCSC_RELEASE: return "PCSC_RELEASE";
|
case OP_CLEAR:
|
||||||
case OP_PCSC_LIST_READERS: return "PCSC_LIST_READERS";
|
return "CLEAR";
|
||||||
case OP_PCSC_CONNECT: return "PCSC_CONNECT";
|
case OP_ENUMERATE:
|
||||||
case OP_PCSC_DISCONNECT: return "PCSC_DISCONNECT";
|
return "ENUMERATE";
|
||||||
case OP_PCSC_TRANSMIT: return "PCSC_TRANSMIT";
|
case OP_ZIP:
|
||||||
case OP_PCRE2_TEST: return "PCRE2_TEST";
|
return "ZIP";
|
||||||
case OP_PCRE2_MATCH: return "PCRE2_MATCH";
|
case OP_MIN:
|
||||||
case OP_PCRE2_FINDALL: return "PCRE2_FINDALL";
|
return "MIN";
|
||||||
case OP_OPENSSL_MD5: return "OPENSSL_MD5";
|
case OP_MAX:
|
||||||
case OP_OPENSSL_SHA256: return "OPENSSL_SHA256";
|
return "MAX";
|
||||||
case OP_OPENSSL_SHA512: return "OPENSSL_SHA512";
|
case OP_CLAMP:
|
||||||
case OP_OPENSSL_RIPEMD160: return "OPENSSL_RIPEMD160";
|
return "CLAMP";
|
||||||
case OP_LIBRESSL_MD5: return "LIBRESSL_MD5";
|
case OP_ABS:
|
||||||
case OP_LIBRESSL_SHA256: return "LIBRESSL_SHA256";
|
return "ABS";
|
||||||
case OP_LIBRESSL_SHA512: return "LIBRESSL_SHA512";
|
case OP_POW:
|
||||||
case OP_LIBRESSL_RIPEMD160: return "LIBRESSL_RIPEMD160";
|
return "POW";
|
||||||
case OP_INI_LOAD: return "INI_LOAD";
|
case OP_RANDOM_SEED:
|
||||||
case OP_INI_FREE: return "INI_FREE";
|
return "RANDOM_SEED";
|
||||||
case OP_INI_GET_STRING: return "INI_GET_STRING";
|
case OP_RANDOM_INT:
|
||||||
case OP_INI_GET_INT: return "INI_GET_INT";
|
return "RANDOM_INT";
|
||||||
case OP_INI_GET_DOUBLE: return "INI_GET_DOUBLE";
|
case OP_MAKE_MAP:
|
||||||
case OP_INI_GET_BOOL: return "INI_GET_BOOL";
|
return "MAKE_MAP";
|
||||||
case OP_INI_SET: return "INI_SET";
|
case OP_KEYS:
|
||||||
case OP_INI_UNSET: return "INI_UNSET";
|
return "KEYS";
|
||||||
case OP_INI_SAVE: return "INI_SAVE";
|
case OP_VALUES:
|
||||||
case OP_XML_PARSE: return "XML_PARSE";
|
return "VALUES";
|
||||||
case OP_XML_ROOT: return "XML_ROOT";
|
case OP_HAS_KEY:
|
||||||
case OP_XML_NAME: return "XML_NAME";
|
return "HAS_KEY";
|
||||||
case OP_XML_TEXT: return "XML_TEXT";
|
case OP_READ_FILE:
|
||||||
case OP_TK_EVAL: return "TK_EVAL";
|
return "READ_FILE";
|
||||||
case OP_TK_RESULT: return "TK_RESULT";
|
case OP_WRITE_FILE:
|
||||||
case OP_TK_LOOP: return "TK_LOOP";
|
return "WRITE_FILE";
|
||||||
case OP_TK_WM_TITLE: return "TK_WM_TITLE";
|
case OP_ENV:
|
||||||
case OP_TK_LABEL: return "TK_LABEL";
|
return "ENV";
|
||||||
case OP_TK_BUTTON: return "TK_BUTTON";
|
case OP_INPUT_LINE:
|
||||||
case OP_TK_PACK: return "TK_PACK";
|
return "INPUT_LINE";
|
||||||
case OP_FLOOR: return "FLOOR";
|
case OP_PROC_RUN:
|
||||||
case OP_CEIL: return "CEIL";
|
return "PROC_RUN";
|
||||||
case OP_TRUNC: return "TRUNC";
|
case OP_PROC_SYSTEM:
|
||||||
case OP_ROUND: return "ROUND";
|
return "PROC_SYSTEM";
|
||||||
case OP_SIN: return "SIN";
|
case OP_TIME_NOW_MS:
|
||||||
case OP_COS: return "COS";
|
return "TIME_NOW_MS";
|
||||||
case OP_TAN: return "TAN";
|
case OP_CLOCK_MONO_MS:
|
||||||
case OP_EXP: return "EXP";
|
return "CLOCK_MONO_MS";
|
||||||
case OP_LOG: return "LOG";
|
case OP_DATE_FORMAT:
|
||||||
case OP_LOG10: return "LOG10";
|
return "DATE_FORMAT";
|
||||||
case OP_SQRT: return "SQRT";
|
case OP_ENV_ALL:
|
||||||
case OP_GCD: return "GCD";
|
return "ENV_ALL";
|
||||||
case OP_LCM: return "LCM";
|
case OP_FUN_VERSION:
|
||||||
case OP_ISQRT: return "ISQRT";
|
return "FUN_VERSION";
|
||||||
case OP_SIGN: return "SIGN";
|
case OP_THREAD_SPAWN:
|
||||||
case OP_FMIN: return "FMIN";
|
return "THREAD_SPAWN";
|
||||||
case OP_FMAX: return "FMAX";
|
case OP_THREAD_JOIN:
|
||||||
case OP_RUST_HELLO: return "RUST_HELLO";
|
return "THREAD_JOIN";
|
||||||
case OP_RUST_HELLO_ARGS: return "RUST_HELLO_ARGS";
|
case OP_SLEEP_MS:
|
||||||
case OP_RUST_HELLO_ARGS_RETURN: return "RUST_HELLO_ARGS_RETURN";
|
return "SLEEP_MS";
|
||||||
case OP_RUST_GET_SP: return "RUST_GET_SP";
|
case OP_RANDOM_NUMBER:
|
||||||
case OP_RUST_SET_EXIT: return "RUST_SET_EXIT";
|
return "RANDOM_NUMBER";
|
||||||
default: return "???";
|
case OP_BAND:
|
||||||
|
return "BAND";
|
||||||
|
case OP_BOR:
|
||||||
|
return "BOR";
|
||||||
|
case OP_BXOR:
|
||||||
|
return "BXOR";
|
||||||
|
case OP_BNOT:
|
||||||
|
return "BNOT";
|
||||||
|
case OP_SHL:
|
||||||
|
return "SHL";
|
||||||
|
case OP_SHR:
|
||||||
|
return "SHR";
|
||||||
|
case OP_ROTL:
|
||||||
|
return "ROTL";
|
||||||
|
case OP_ROTR:
|
||||||
|
return "ROTR";
|
||||||
|
case OP_JSON_PARSE:
|
||||||
|
return "JSON_PARSE";
|
||||||
|
case OP_JSON_STRINGIFY:
|
||||||
|
return "JSON_STRINGIFY";
|
||||||
|
case OP_JSON_FROM_FILE:
|
||||||
|
return "JSON_FROM_FILE";
|
||||||
|
case OP_JSON_TO_FILE:
|
||||||
|
return "JSON_TO_FILE";
|
||||||
|
case OP_CURL_GET:
|
||||||
|
return "CURL_GET";
|
||||||
|
case OP_CURL_POST:
|
||||||
|
return "CURL_POST";
|
||||||
|
case OP_CURL_DOWNLOAD:
|
||||||
|
return "CURL_DOWNLOAD";
|
||||||
|
case OP_SQLITE_OPEN:
|
||||||
|
return "SQLITE_OPEN";
|
||||||
|
case OP_SQLITE_CLOSE:
|
||||||
|
return "SQLITE_CLOSE";
|
||||||
|
case OP_SQLITE_EXEC:
|
||||||
|
return "SQLITE_EXEC";
|
||||||
|
case OP_SQLITE_QUERY:
|
||||||
|
return "SQLITE_QUERY";
|
||||||
|
case OP_LIBSQL_OPEN:
|
||||||
|
return "LIBSQL_OPEN";
|
||||||
|
case OP_LIBSQL_CLOSE:
|
||||||
|
return "LIBSQL_CLOSE";
|
||||||
|
case OP_LIBSQL_EXEC:
|
||||||
|
return "LIBSQL_EXEC";
|
||||||
|
case OP_LIBSQL_QUERY:
|
||||||
|
return "LIBSQL_QUERY";
|
||||||
|
case OP_PCSC_ESTABLISH:
|
||||||
|
return "PCSC_ESTABLISH";
|
||||||
|
case OP_PCSC_RELEASE:
|
||||||
|
return "PCSC_RELEASE";
|
||||||
|
case OP_PCSC_LIST_READERS:
|
||||||
|
return "PCSC_LIST_READERS";
|
||||||
|
case OP_PCSC_CONNECT:
|
||||||
|
return "PCSC_CONNECT";
|
||||||
|
case OP_PCSC_DISCONNECT:
|
||||||
|
return "PCSC_DISCONNECT";
|
||||||
|
case OP_PCSC_TRANSMIT:
|
||||||
|
return "PCSC_TRANSMIT";
|
||||||
|
case OP_PCRE2_TEST:
|
||||||
|
return "PCRE2_TEST";
|
||||||
|
case OP_PCRE2_MATCH:
|
||||||
|
return "PCRE2_MATCH";
|
||||||
|
case OP_PCRE2_FINDALL:
|
||||||
|
return "PCRE2_FINDALL";
|
||||||
|
case OP_OPENSSL_MD5:
|
||||||
|
return "OPENSSL_MD5";
|
||||||
|
case OP_OPENSSL_SHA256:
|
||||||
|
return "OPENSSL_SHA256";
|
||||||
|
case OP_OPENSSL_SHA512:
|
||||||
|
return "OPENSSL_SHA512";
|
||||||
|
case OP_OPENSSL_RIPEMD160:
|
||||||
|
return "OPENSSL_RIPEMD160";
|
||||||
|
case OP_LIBRESSL_MD5:
|
||||||
|
return "LIBRESSL_MD5";
|
||||||
|
case OP_LIBRESSL_SHA256:
|
||||||
|
return "LIBRESSL_SHA256";
|
||||||
|
case OP_LIBRESSL_SHA512:
|
||||||
|
return "LIBRESSL_SHA512";
|
||||||
|
case OP_LIBRESSL_RIPEMD160:
|
||||||
|
return "LIBRESSL_RIPEMD160";
|
||||||
|
case OP_INI_LOAD:
|
||||||
|
return "INI_LOAD";
|
||||||
|
case OP_INI_FREE:
|
||||||
|
return "INI_FREE";
|
||||||
|
case OP_INI_GET_STRING:
|
||||||
|
return "INI_GET_STRING";
|
||||||
|
case OP_INI_GET_INT:
|
||||||
|
return "INI_GET_INT";
|
||||||
|
case OP_INI_GET_DOUBLE:
|
||||||
|
return "INI_GET_DOUBLE";
|
||||||
|
case OP_INI_GET_BOOL:
|
||||||
|
return "INI_GET_BOOL";
|
||||||
|
case OP_INI_SET:
|
||||||
|
return "INI_SET";
|
||||||
|
case OP_INI_UNSET:
|
||||||
|
return "INI_UNSET";
|
||||||
|
case OP_INI_SAVE:
|
||||||
|
return "INI_SAVE";
|
||||||
|
case OP_XML_PARSE:
|
||||||
|
return "XML_PARSE";
|
||||||
|
case OP_XML_ROOT:
|
||||||
|
return "XML_ROOT";
|
||||||
|
case OP_XML_NAME:
|
||||||
|
return "XML_NAME";
|
||||||
|
case OP_XML_TEXT:
|
||||||
|
return "XML_TEXT";
|
||||||
|
case OP_TK_EVAL:
|
||||||
|
return "TK_EVAL";
|
||||||
|
case OP_TK_RESULT:
|
||||||
|
return "TK_RESULT";
|
||||||
|
case OP_TK_LOOP:
|
||||||
|
return "TK_LOOP";
|
||||||
|
case OP_TK_WM_TITLE:
|
||||||
|
return "TK_WM_TITLE";
|
||||||
|
case OP_TK_LABEL:
|
||||||
|
return "TK_LABEL";
|
||||||
|
case OP_TK_BUTTON:
|
||||||
|
return "TK_BUTTON";
|
||||||
|
case OP_TK_PACK:
|
||||||
|
return "TK_PACK";
|
||||||
|
case OP_FLOOR:
|
||||||
|
return "FLOOR";
|
||||||
|
case OP_CEIL:
|
||||||
|
return "CEIL";
|
||||||
|
case OP_TRUNC:
|
||||||
|
return "TRUNC";
|
||||||
|
case OP_ROUND:
|
||||||
|
return "ROUND";
|
||||||
|
case OP_SIN:
|
||||||
|
return "SIN";
|
||||||
|
case OP_COS:
|
||||||
|
return "COS";
|
||||||
|
case OP_TAN:
|
||||||
|
return "TAN";
|
||||||
|
case OP_EXP:
|
||||||
|
return "EXP";
|
||||||
|
case OP_LOG:
|
||||||
|
return "LOG";
|
||||||
|
case OP_LOG10:
|
||||||
|
return "LOG10";
|
||||||
|
case OP_SQRT:
|
||||||
|
return "SQRT";
|
||||||
|
case OP_GCD:
|
||||||
|
return "GCD";
|
||||||
|
case OP_LCM:
|
||||||
|
return "LCM";
|
||||||
|
case OP_ISQRT:
|
||||||
|
return "ISQRT";
|
||||||
|
case OP_SIGN:
|
||||||
|
return "SIGN";
|
||||||
|
case OP_FMIN:
|
||||||
|
return "FMIN";
|
||||||
|
case OP_FMAX:
|
||||||
|
return "FMAX";
|
||||||
|
case OP_RUST_HELLO:
|
||||||
|
return "RUST_HELLO";
|
||||||
|
case OP_RUST_HELLO_ARGS:
|
||||||
|
return "RUST_HELLO_ARGS";
|
||||||
|
case OP_RUST_HELLO_ARGS_RETURN:
|
||||||
|
return "RUST_HELLO_ARGS_RETURN";
|
||||||
|
case OP_RUST_GET_SP:
|
||||||
|
return "RUST_GET_SP";
|
||||||
|
case OP_RUST_SET_EXIT:
|
||||||
|
return "RUST_SET_EXIT";
|
||||||
|
default:
|
||||||
|
return "???";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
#ifndef FUN_BYTECODE_H
|
#ifndef FUN_BYTECODE_H
|
||||||
#define FUN_BYTECODE_H
|
#define FUN_BYTECODE_H
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// VM opcodes
|
// VM opcodes
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
|
||||||
15
src/external/curl.c
vendored
15
src/external/curl.c
vendored
|
|
@ -12,18 +12,23 @@
|
||||||
/* Ensure libcurl headers and helpers are defined at file scope (not inside vm_run) */
|
/* Ensure libcurl headers and helpers are defined at file scope (not inside vm_run) */
|
||||||
#ifdef FUN_WITH_CURL
|
#ifdef FUN_WITH_CURL
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
typedef struct { char *d; size_t n; } FunCurlBuf;
|
typedef struct {
|
||||||
|
char *d;
|
||||||
|
size_t n;
|
||||||
|
} FunCurlBuf;
|
||||||
static size_t fun_curl_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
|
static size_t fun_curl_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
|
||||||
size_t add = sz * nm;
|
size_t add = sz * nm;
|
||||||
FunCurlBuf *b = (FunCurlBuf*)ud;
|
FunCurlBuf *b = (FunCurlBuf *)ud;
|
||||||
char *p = (char*)realloc(b->d, b->n + add + 1);
|
char *p = (char *)realloc(b->d, b->n + add + 1);
|
||||||
if (!p) return 0;
|
if (!p) return 0;
|
||||||
memcpy(p + b->n, ptr, add);
|
memcpy(p + b->n, ptr, add);
|
||||||
b->d = p; b->n += add; b->d[b->n] = '\0';
|
b->d = p;
|
||||||
|
b->n += add;
|
||||||
|
b->d[b->n] = '\0';
|
||||||
return add;
|
return add;
|
||||||
}
|
}
|
||||||
static size_t fun_curl_file_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
|
static size_t fun_curl_file_write_cb(void *ptr, size_t sz, size_t nm, void *ud) {
|
||||||
FILE *f = (FILE*)ud;
|
FILE *f = (FILE *)ud;
|
||||||
return fwrite(ptr, sz, nm, f);
|
return fwrite(ptr, sz, nm, f);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
22
src/external/ini.c
vendored
22
src/external/ini.c
vendored
|
|
@ -11,18 +11,18 @@
|
||||||
|
|
||||||
#ifdef FUN_WITH_INI
|
#ifdef FUN_WITH_INI
|
||||||
#if defined(__has_include)
|
#if defined(__has_include)
|
||||||
# if __has_include(<iniparser/iniparser.h>)
|
#if __has_include(<iniparser/iniparser.h>)
|
||||||
# include <iniparser/iniparser.h>
|
#include <iniparser/dictionary.h>
|
||||||
# include <iniparser/dictionary.h>
|
#include <iniparser/iniparser.h>
|
||||||
# elif __has_include(<iniparser.h>)
|
#elif __has_include(<iniparser.h>)
|
||||||
# include <iniparser.h>
|
#include <dictionary.h>
|
||||||
# include <dictionary.h>
|
#include <iniparser.h>
|
||||||
# else
|
|
||||||
# error "iniparser headers not found"
|
|
||||||
# endif
|
|
||||||
#else
|
#else
|
||||||
# include <iniparser/iniparser.h>
|
#error "iniparser headers not found"
|
||||||
# include <iniparser/dictionary.h>
|
#endif
|
||||||
|
#else
|
||||||
|
#include <iniparser/dictionary.h>
|
||||||
|
#include <iniparser/iniparser.h>
|
||||||
#endif
|
#endif
|
||||||
#include "vm/ini/handles.h"
|
#include "vm/ini/handles.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
39
src/external/json.c
vendored
39
src/external/json.c
vendored
|
|
@ -9,7 +9,7 @@
|
||||||
* Added: 2025-12-11 (2025-12-11 migrated from src/vm.c)
|
* Added: 2025-12-11 (2025-12-11 migrated from src/vm.c)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* json-c helpers and VM opcode cases (included from vm.c) */
|
/* json-c helpers and VM opcode cases (included from vm.c) */
|
||||||
|
|
||||||
#ifdef FUN_WITH_JSON
|
#ifdef FUN_WITH_JSON
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
|
@ -23,24 +23,30 @@ static Value json_to_fun(json_object *j) {
|
||||||
if (!j) return make_nil();
|
if (!j) return make_nil();
|
||||||
enum json_type t = json_object_get_type(j);
|
enum json_type t = json_object_get_type(j);
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case json_type_null: return make_nil();
|
case json_type_null:
|
||||||
case json_type_boolean: return make_bool(json_object_get_boolean(j));
|
return make_nil();
|
||||||
case json_type_double: return make_float(json_object_get_double(j));
|
case json_type_boolean:
|
||||||
case json_type_int: return make_int((int64_t)json_object_get_int64(j));
|
return make_bool(json_object_get_boolean(j));
|
||||||
case json_type_string: return make_string(json_object_get_string(j));
|
case json_type_double:
|
||||||
|
return make_float(json_object_get_double(j));
|
||||||
|
case json_type_int:
|
||||||
|
return make_int((int64_t)json_object_get_int64(j));
|
||||||
|
case json_type_string:
|
||||||
|
return make_string(json_object_get_string(j));
|
||||||
case json_type_array: {
|
case json_type_array: {
|
||||||
size_t n = json_object_array_length(j);
|
size_t n = json_object_array_length(j);
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return make_array_from_values(NULL, 0);
|
return make_array_from_values(NULL, 0);
|
||||||
}
|
}
|
||||||
Value *vals = (Value*)malloc(sizeof(Value) * n);
|
Value *vals = (Value *)malloc(sizeof(Value) * n);
|
||||||
if (!vals) return make_array_from_values(NULL, 0);
|
if (!vals) return make_array_from_values(NULL, 0);
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
json_object *item = json_object_array_get_idx(j, (int)i);
|
json_object *item = json_object_array_get_idx(j, (int)i);
|
||||||
vals[i] = json_to_fun(item);
|
vals[i] = json_to_fun(item);
|
||||||
}
|
}
|
||||||
Value arr = make_array_from_values(vals, (int)n);
|
Value arr = make_array_from_values(vals, (int)n);
|
||||||
for (size_t i = 0; i < n; ++i) free_value(vals[i]);
|
for (size_t i = 0; i < n; ++i)
|
||||||
|
free_value(vals[i]);
|
||||||
free(vals);
|
free(vals);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
@ -56,13 +62,18 @@ static Value json_to_fun(json_object *j) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static json_object* fun_to_json(const Value *v) {
|
static json_object *fun_to_json(const Value *v) {
|
||||||
switch (v->type) {
|
switch (v->type) {
|
||||||
case VAL_NIL: return json_object_new_null();
|
case VAL_NIL:
|
||||||
case VAL_BOOL: return json_object_new_boolean(v->i ? 1 : 0);
|
return json_object_new_null();
|
||||||
case VAL_INT: return json_object_new_int64(v->i);
|
case VAL_BOOL:
|
||||||
case VAL_FLOAT: return json_object_new_double(v->d);
|
return json_object_new_boolean(v->i ? 1 : 0);
|
||||||
case VAL_STRING: return json_object_new_string(v->s ? v->s : "");
|
case VAL_INT:
|
||||||
|
return json_object_new_int64(v->i);
|
||||||
|
case VAL_FLOAT:
|
||||||
|
return json_object_new_double(v->d);
|
||||||
|
case VAL_STRING:
|
||||||
|
return json_object_new_string(v->s ? v->s : "");
|
||||||
case VAL_ARRAY: {
|
case VAL_ARRAY: {
|
||||||
json_object *arr = json_object_new_array();
|
json_object *arr = json_object_new_array();
|
||||||
int n = array_length(v);
|
int n = array_length(v);
|
||||||
|
|
|
||||||
82
src/external/libressl.c
vendored
82
src/external/libressl.c
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -40,7 +40,7 @@ static char *fun_libressl_md5_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -49,18 +49,24 @@ static char *fun_libressl_md5_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -75,7 +81,7 @@ static char *fun_libressl_sha256_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -84,18 +90,24 @@ static char *fun_libressl_sha256_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -110,7 +122,7 @@ static char *fun_libressl_sha512_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -119,18 +131,24 @@ static char *fun_libressl_sha512_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -145,7 +163,7 @@ static char *fun_libressl_ripemd160_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -154,18 +172,24 @@ static char *fun_libressl_ripemd160_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
11
src/external/libsql.c
vendored
11
src/external/libsql.c
vendored
|
|
@ -10,10 +10,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef FUN_WITH_LIBSQL
|
#ifdef FUN_WITH_LIBSQL
|
||||||
|
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <sqlite3.h> /* libsql provides a sqlite3-compatible C API */
|
|
||||||
|
|
||||||
typedef struct LibSqlHandle {
|
typedef struct LibSqlHandle {
|
||||||
int id;
|
int id;
|
||||||
|
|
@ -25,7 +25,7 @@ static LibSqlHandle *g_libsql_handles = NULL;
|
||||||
static int g_libsql_next_id = 1;
|
static int g_libsql_next_id = 1;
|
||||||
|
|
||||||
static LibSqlHandle *libsql_reg_add(sqlite3 *db) {
|
static LibSqlHandle *libsql_reg_add(sqlite3 *db) {
|
||||||
LibSqlHandle *h = (LibSqlHandle*)malloc(sizeof(LibSqlHandle));
|
LibSqlHandle *h = (LibSqlHandle *)malloc(sizeof(LibSqlHandle));
|
||||||
if (!h) return NULL;
|
if (!h) return NULL;
|
||||||
h->id = g_libsql_next_id++;
|
h->id = g_libsql_next_id++;
|
||||||
h->db = db;
|
h->db = db;
|
||||||
|
|
@ -36,7 +36,10 @@ static LibSqlHandle *libsql_reg_add(sqlite3 *db) {
|
||||||
|
|
||||||
static LibSqlHandle *libsql_reg_get(int id) {
|
static LibSqlHandle *libsql_reg_get(int id) {
|
||||||
LibSqlHandle *p = g_libsql_handles;
|
LibSqlHandle *p = g_libsql_handles;
|
||||||
while (p) { if (p->id == id) return p; p = p->next; }
|
while (p) {
|
||||||
|
if (p->id == id) return p;
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
84
src/external/openssl.c
vendored
84
src/external/openssl.c
vendored
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
* Added: 2026-02-19
|
* Added: 2026-02-19
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OpenSSL integration helpers (MD5, RIPEMD-160, SHA-256, SHA-512)
|
* OpenSSL integration helpers (MD5, RIPEMD-160, SHA-256, SHA-512)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -45,18 +45,24 @@ static char *fun_openssl_md5_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -72,7 +78,7 @@ static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -81,18 +87,24 @@ static char *fun_openssl_sha256_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -108,7 +120,7 @@ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -117,18 +129,24 @@ static char *fun_openssl_sha512_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -146,7 +164,7 @@ static char *fun_openssl_ripemd160_hex(const unsigned char *data, size_t len) {
|
||||||
if (!md) return NULL;
|
if (!md) return NULL;
|
||||||
int dlen = EVP_MD_get_size(md);
|
int dlen = EVP_MD_get_size(md);
|
||||||
if (dlen <= 0) return NULL;
|
if (dlen <= 0) return NULL;
|
||||||
unsigned char *digest = (unsigned char*)malloc((size_t)dlen);
|
unsigned char *digest = (unsigned char *)malloc((size_t)dlen);
|
||||||
if (!digest) return NULL;
|
if (!digest) return NULL;
|
||||||
unsigned int out_len = 0;
|
unsigned int out_len = 0;
|
||||||
int ok;
|
int ok;
|
||||||
|
|
@ -155,18 +173,24 @@ static char *fun_openssl_ripemd160_hex(const unsigned char *data, size_t len) {
|
||||||
} else {
|
} else {
|
||||||
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
ok = EVP_Digest(data, len, digest, &out_len, md, NULL);
|
||||||
}
|
}
|
||||||
if (ok != 1 || (int)out_len != dlen) { free(digest); return NULL; }
|
if (ok != 1 || (int)out_len != dlen) {
|
||||||
char *hex = (char*)malloc((size_t)dlen * 2 + 1);
|
free(digest);
|
||||||
if (!hex) { free(digest); return NULL; }
|
return NULL;
|
||||||
|
}
|
||||||
|
char *hex = (char *)malloc((size_t)dlen * 2 + 1);
|
||||||
|
if (!hex) {
|
||||||
|
free(digest);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
for (int i = 0; i < dlen; ++i) {
|
for (int i = 0; i < dlen; ++i) {
|
||||||
hex[2*i] = hexdig[(digest[i] >> 4) & 0xF];
|
hex[2 * i] = hexdig[(digest[i] >> 4) & 0xF];
|
||||||
hex[2*i+1] = hexdig[digest[i] & 0xF];
|
hex[2 * i + 1] = hexdig[digest[i] & 0xF];
|
||||||
}
|
}
|
||||||
hex[dlen * 2] = '\0';
|
hex[dlen * 2] = '\0';
|
||||||
free(digest);
|
free(digest);
|
||||||
return hex;
|
return hex;
|
||||||
#else
|
#else
|
||||||
char *hex = (char*)malloc(1);
|
char *hex = (char *)malloc(1);
|
||||||
if (hex) hex[0] = '\0';
|
if (hex) hex[0] = '\0';
|
||||||
return hex;
|
return hex;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
2
src/external/pcre2.c
vendored
2
src/external/pcre2.c
vendored
|
|
@ -9,7 +9,7 @@
|
||||||
* Added: 2025-12-11 (2025-12-11 migrated from src/vm/libsql/common.c)
|
* Added: 2025-12-11 (2025-12-11 migrated from src/vm/libsql/common.c)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Ensure PCRE2 is configured consistently across the whole translation unit.
|
/* Ensure PCRE2 is configured consistently across the whole translation unit.
|
||||||
* vm.c includes many opcode implementation .c files; some use PCRE2. For PCRE2
|
* vm.c includes many opcode implementation .c files; some use PCRE2. For PCRE2
|
||||||
* headers to expose the correct typedefs (e.g., pcre2_code, PCRE2_SPTR), the
|
* headers to expose the correct typedefs (e.g., pcre2_code, PCRE2_SPTR), the
|
||||||
* PCRE2_CODE_UNIT_WIDTH macro must be defined before the first inclusion of
|
* PCRE2_CODE_UNIT_WIDTH macro must be defined before the first inclusion of
|
||||||
|
|
|
||||||
55
src/external/pcsc.c
vendored
55
src/external/pcsc.c
vendored
|
|
@ -16,24 +16,24 @@ Included the file scope from vm.c.
|
||||||
|
|
||||||
#ifdef FUN_WITH_PCSC
|
#ifdef FUN_WITH_PCSC
|
||||||
#if defined(__has_include)
|
#if defined(__has_include)
|
||||||
#if __has_include(<PCSC/winscard.h>)
|
#if __has_include(<PCSC/winscard.h>)
|
||||||
#include <PCSC/winscard.h>
|
#include <PCSC/winscard.h>
|
||||||
#include <PCSC/wintypes.h>
|
#include <PCSC/wintypes.h>
|
||||||
#elif __has_include(<winscard.h>)
|
#elif __has_include(<winscard.h>)
|
||||||
#include <winscard.h>
|
#include <winscard.h>
|
||||||
#else
|
#else
|
||||||
#error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
|
#error "FUN_WITH_PCSC is enabled but PCSC headers were not found"
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#include <PCSC/winscard.h>
|
#include <PCSC/winscard.h>
|
||||||
#include <PCSC/wintypes.h>
|
#include <PCSC/wintypes.h>
|
||||||
#endif
|
#endif
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SCARDCONTEXT ctx;
|
SCARDCONTEXT ctx;
|
||||||
int in_use;
|
int in_use;
|
||||||
} pcsc_ctx_entry;
|
} pcsc_ctx_entry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SCARDHANDLE h;
|
SCARDHANDLE h;
|
||||||
|
|
@ -45,31 +45,40 @@ static pcsc_ctx_entry g_pcsc_ctx[8];
|
||||||
static pcsc_card_entry g_pcsc_card[32];
|
static pcsc_card_entry g_pcsc_card[32];
|
||||||
|
|
||||||
static int pcsc_alloc_ctx_slot(void) {
|
static int pcsc_alloc_ctx_slot(void) {
|
||||||
for (int i = 0; i < (int)(sizeof(g_pcsc_ctx)/sizeof(g_pcsc_ctx[0])); ++i) {
|
for (int i = 0; i < (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0])); ++i) {
|
||||||
if (!g_pcsc_ctx[i].in_use) { g_pcsc_ctx[i].in_use = 1; g_pcsc_ctx[i].ctx = 0; return i + 1; }
|
if (!g_pcsc_ctx[i].in_use) {
|
||||||
|
g_pcsc_ctx[i].in_use = 1;
|
||||||
|
g_pcsc_ctx[i].ctx = 0;
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pcsc_alloc_card_slot(void) {
|
static int pcsc_alloc_card_slot(void) {
|
||||||
for (int i = 0; i < (int)(sizeof(g_pcsc_card)/sizeof(g_pcsc_card[0])); ++i) {
|
for (int i = 0; i < (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0])); ++i) {
|
||||||
if (!g_pcsc_card[i].in_use) { g_pcsc_card[i].in_use = 1; g_pcsc_card[i].h = 0; g_pcsc_card[i].proto = 0; return i + 1; }
|
if (!g_pcsc_card[i].in_use) {
|
||||||
|
g_pcsc_card[i].in_use = 1;
|
||||||
|
g_pcsc_card[i].h = 0;
|
||||||
|
g_pcsc_card[i].proto = 0;
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pcsc_ctx_entry* pcsc_get_ctx(int id) {
|
static pcsc_ctx_entry *pcsc_get_ctx(int id) {
|
||||||
if (id <= 0) return NULL;
|
if (id <= 0) return NULL;
|
||||||
int idx = id - 1;
|
int idx = id - 1;
|
||||||
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx)/sizeof(g_pcsc_ctx[0]))) return NULL;
|
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_ctx) / sizeof(g_pcsc_ctx[0]))) return NULL;
|
||||||
if (!g_pcsc_ctx[idx].in_use) return NULL;
|
if (!g_pcsc_ctx[idx].in_use) return NULL;
|
||||||
return &g_pcsc_ctx[idx];
|
return &g_pcsc_ctx[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
static pcsc_card_entry* pcsc_get_card(int id) {
|
static pcsc_card_entry *pcsc_get_card(int id) {
|
||||||
if (id <= 0) return NULL;
|
if (id <= 0) return NULL;
|
||||||
int idx = id - 1;
|
int idx = id - 1;
|
||||||
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card)/sizeof(g_pcsc_card[0]))) return NULL;
|
if (idx < 0 || idx >= (int)(sizeof(g_pcsc_card) / sizeof(g_pcsc_card[0]))) return NULL;
|
||||||
if (!g_pcsc_card[idx].in_use) return NULL;
|
if (!g_pcsc_card[idx].in_use) return NULL;
|
||||||
return &g_pcsc_card[idx];
|
return &g_pcsc_card[idx];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/external/sqlite.c
vendored
16
src/external/sqlite.c
vendored
|
|
@ -24,8 +24,8 @@ typedef struct SqlHandle {
|
||||||
static SqlHandle *g_sql_handles = NULL;
|
static SqlHandle *g_sql_handles = NULL;
|
||||||
static int g_sql_next_id = 1;
|
static int g_sql_next_id = 1;
|
||||||
|
|
||||||
static SqlHandle* sql_reg_add(sqlite3 *db) {
|
static SqlHandle *sql_reg_add(sqlite3 *db) {
|
||||||
SqlHandle *h = (SqlHandle*)calloc(1, sizeof(SqlHandle));
|
SqlHandle *h = (SqlHandle *)calloc(1, sizeof(SqlHandle));
|
||||||
if (!h) return NULL;
|
if (!h) return NULL;
|
||||||
h->id = g_sql_next_id++;
|
h->id = g_sql_next_id++;
|
||||||
h->db = db;
|
h->db = db;
|
||||||
|
|
@ -34,15 +34,21 @@ static SqlHandle* sql_reg_add(sqlite3 *db) {
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SqlHandle* sql_reg_get(int id) {
|
static SqlHandle *sql_reg_get(int id) {
|
||||||
for (SqlHandle *p = g_sql_handles; p; p = p->next) if (p->id == id) return p;
|
for (SqlHandle *p = g_sql_handles; p; p = p->next)
|
||||||
|
if (p->id == id) return p;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sql_reg_del(int id) {
|
static void sql_reg_del(int id) {
|
||||||
SqlHandle **pp = &g_sql_handles;
|
SqlHandle **pp = &g_sql_handles;
|
||||||
while (*pp) {
|
while (*pp) {
|
||||||
if ((*pp)->id == id) { SqlHandle *d = *pp; *pp = d->next; free(d); return; }
|
if ((*pp)->id == id) {
|
||||||
|
SqlHandle *d = *pp;
|
||||||
|
*pp = d->next;
|
||||||
|
free(d);
|
||||||
|
return;
|
||||||
|
}
|
||||||
pp = &(*pp)->next;
|
pp = &(*pp)->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
src/external/tcltk.c
vendored
24
src/external/tcltk.c
vendored
|
|
@ -12,7 +12,7 @@
|
||||||
#ifdef FUN_WITH_TCLTK
|
#ifdef FUN_WITH_TCLTK
|
||||||
#include <tcl.h>
|
#include <tcl.h>
|
||||||
#include <tk.h>
|
#include <tk.h>
|
||||||
static Tcl_Interp* g_fun_tcl_interp = NULL;
|
static Tcl_Interp *g_fun_tcl_interp = NULL;
|
||||||
|
|
||||||
static void fun_tk_init_once(void) {
|
static void fun_tk_init_once(void) {
|
||||||
if (g_fun_tcl_interp) return;
|
if (g_fun_tcl_interp) return;
|
||||||
|
|
@ -37,7 +37,7 @@ static int fun_tk_eval_script(const char *script) {
|
||||||
return rc; /* TCL_OK = 0 */
|
return rc; /* TCL_OK = 0 */
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* fun_tk_get_result(void) {
|
static const char *fun_tk_get_result(void) {
|
||||||
fun_tk_init_once();
|
fun_tk_init_once();
|
||||||
if (!g_fun_tcl_interp) return "";
|
if (!g_fun_tcl_interp) return "";
|
||||||
return Tcl_GetStringResult(g_fun_tcl_interp);
|
return Tcl_GetStringResult(g_fun_tcl_interp);
|
||||||
|
|
@ -48,7 +48,8 @@ static void fun_tk_loop(void) {
|
||||||
if (!g_fun_tcl_interp) return;
|
if (!g_fun_tcl_interp) return;
|
||||||
/* Drive Tk event loop until all main windows are closed */
|
/* Drive Tk event loop until all main windows are closed */
|
||||||
while (Tk_GetNumMainWindows() > 0) {
|
while (Tk_GetNumMainWindows() > 0) {
|
||||||
while (Tcl_DoOneEvent(0)) {}
|
while (Tcl_DoOneEvent(0)) {
|
||||||
|
}
|
||||||
/* tiny sleep to avoid busy spin */
|
/* tiny sleep to avoid busy spin */
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
@ -62,8 +63,17 @@ static void fun_tk_loop(void) {
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Stubs when Tcl/Tk is disabled */
|
/* Stubs when Tcl/Tk is disabled */
|
||||||
static void fun_tk_init_once(void) { (void)0; }
|
static void fun_tk_init_once(void) {
|
||||||
static int fun_tk_eval_script(const char *script) { (void)script; return -1; }
|
(void)0;
|
||||||
static const char* fun_tk_get_result(void) { return ""; }
|
}
|
||||||
static void fun_tk_loop(void) { (void)0; }
|
static int fun_tk_eval_script(const char *script) {
|
||||||
|
(void)script;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
static const char *fun_tk_get_result(void) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
static void fun_tk_loop(void) {
|
||||||
|
(void)0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
34
src/external/xml2.c
vendored
34
src/external/xml2.c
vendored
|
|
@ -13,24 +13,34 @@
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
typedef struct { xmlDocPtr doc; int in_use; } XmlDocSlot;
|
typedef struct {
|
||||||
typedef struct { xmlNodePtr node; int in_use; } XmlNodeSlot;
|
xmlDocPtr doc;
|
||||||
|
int in_use;
|
||||||
|
} XmlDocSlot;
|
||||||
|
typedef struct {
|
||||||
|
xmlNodePtr node;
|
||||||
|
int in_use;
|
||||||
|
} XmlNodeSlot;
|
||||||
|
|
||||||
static XmlDocSlot g_xml_docs[64];
|
static XmlDocSlot g_xml_docs[64];
|
||||||
static XmlNodeSlot g_xml_nodes[256];
|
static XmlNodeSlot g_xml_nodes[256];
|
||||||
|
|
||||||
static int xml_doc_alloc(xmlDocPtr d) {
|
static int xml_doc_alloc(xmlDocPtr d) {
|
||||||
for (int i = 1; i < (int)(sizeof(g_xml_docs)/sizeof(g_xml_docs[0])); ++i) {
|
for (int i = 1; i < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])); ++i) {
|
||||||
if (!g_xml_docs[i].in_use) { g_xml_docs[i].in_use = 1; g_xml_docs[i].doc = d; return i; }
|
if (!g_xml_docs[i].in_use) {
|
||||||
|
g_xml_docs[i].in_use = 1;
|
||||||
|
g_xml_docs[i].doc = d;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static xmlDocPtr xml_doc_get(int h) {
|
static xmlDocPtr xml_doc_get(int h) {
|
||||||
if (h > 0 && h < (int)(sizeof(g_xml_docs)/sizeof(g_xml_docs[0])) && g_xml_docs[h].in_use) return g_xml_docs[h].doc;
|
if (h > 0 && h < (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) && g_xml_docs[h].in_use) return g_xml_docs[h].doc;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
static int xml_doc_free_handle(int h) {
|
static int xml_doc_free_handle(int h) {
|
||||||
if (h <= 0 || h >= (int)(sizeof(g_xml_docs)/sizeof(g_xml_docs[0])) || !g_xml_docs[h].in_use) return 0;
|
if (h <= 0 || h >= (int)(sizeof(g_xml_docs) / sizeof(g_xml_docs[0])) || !g_xml_docs[h].in_use) return 0;
|
||||||
if (g_xml_docs[h].doc) xmlFreeDoc(g_xml_docs[h].doc);
|
if (g_xml_docs[h].doc) xmlFreeDoc(g_xml_docs[h].doc);
|
||||||
g_xml_docs[h].doc = NULL;
|
g_xml_docs[h].doc = NULL;
|
||||||
g_xml_docs[h].in_use = 0;
|
g_xml_docs[h].in_use = 0;
|
||||||
|
|
@ -38,17 +48,21 @@ static int xml_doc_free_handle(int h) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xml_node_alloc(xmlNodePtr n) {
|
static int xml_node_alloc(xmlNodePtr n) {
|
||||||
for (int i = 1; i < (int)(sizeof(g_xml_nodes)/sizeof(g_xml_nodes[0])); ++i) {
|
for (int i = 1; i < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])); ++i) {
|
||||||
if (!g_xml_nodes[i].in_use) { g_xml_nodes[i].in_use = 1; g_xml_nodes[i].node = n; return i; }
|
if (!g_xml_nodes[i].in_use) {
|
||||||
|
g_xml_nodes[i].in_use = 1;
|
||||||
|
g_xml_nodes[i].node = n;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static xmlNodePtr xml_node_get(int h) {
|
static xmlNodePtr xml_node_get(int h) {
|
||||||
if (h > 0 && h < (int)(sizeof(g_xml_nodes)/sizeof(g_xml_nodes[0])) && g_xml_nodes[h].in_use) return g_xml_nodes[h].node;
|
if (h > 0 && h < (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) && g_xml_nodes[h].in_use) return g_xml_nodes[h].node;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
static int xml_node_free_handle(int h) {
|
static int xml_node_free_handle(int h) {
|
||||||
if (h <= 0 || h >= (int)(sizeof(g_xml_nodes)/sizeof(g_xml_nodes[0])) || !g_xml_nodes[h].in_use) return 0;
|
if (h <= 0 || h >= (int)(sizeof(g_xml_nodes) / sizeof(g_xml_nodes[0])) || !g_xml_nodes[h].in_use) return 0;
|
||||||
/* nodes are owned by their document; do not free here */
|
/* nodes are owned by their document; do not free here */
|
||||||
g_xml_nodes[h].node = NULL;
|
g_xml_nodes[h].node = NULL;
|
||||||
g_xml_nodes[h].in_use = 0;
|
g_xml_nodes[h].in_use = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -7,15 +7,15 @@
|
||||||
* https://opensource.org/license/apache-2-0
|
* https://opensource.org/license/apache-2-0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Main entry point for the Fun language interpreter.
|
* Main entry point for the Fun language interpreter.
|
||||||
* Builds a CLI that runs a script file if provided; otherwise starts the REPL
|
* Builds a CLI that runs a script file if provided; otherwise starts the REPL
|
||||||
* when compiled with FUN_WITH_REPL enabled.
|
* when compiled with FUN_WITH_REPL enabled.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bytecode.h"
|
#include "bytecode.h"
|
||||||
#include "vm.h"
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
#include "vm.h"
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -119,7 +119,7 @@ int main(int argc, char **argv) {
|
||||||
for (int i = 0; i < sargc; ++i) {
|
for (int i = 0; i < sargc; ++i) {
|
||||||
total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */
|
total += strlen(argv[sargi + i]) + 1; /* +1 for space or NUL */
|
||||||
}
|
}
|
||||||
char *joined = (char*)malloc(total);
|
char *joined = (char *)malloc(total);
|
||||||
if (joined) {
|
if (joined) {
|
||||||
joined[0] = '\0';
|
joined[0] = '\0';
|
||||||
for (int i = 0; i < sargc; ++i) {
|
for (int i = 0; i < sargc; ++i) {
|
||||||
|
|
|
||||||
|
|
@ -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)) { \
|
||||||
|
|
|
||||||
10
src/iter.c
10
src/iter.c
|
|
@ -14,7 +14,7 @@
|
||||||
Value bi_enumerate(const Value *arr) {
|
Value bi_enumerate(const Value *arr) {
|
||||||
int n = array_length(arr);
|
int n = array_length(arr);
|
||||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||||
Value *pairs = (Value*)malloc(sizeof(Value) * n);
|
Value *pairs = (Value *)malloc(sizeof(Value) * n);
|
||||||
if (!pairs) return make_array_from_values(NULL, 0);
|
if (!pairs) return make_array_from_values(NULL, 0);
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
Value elem;
|
Value elem;
|
||||||
|
|
@ -28,7 +28,8 @@ Value bi_enumerate(const Value *arr) {
|
||||||
pairs[i] = kv;
|
pairs[i] = kv;
|
||||||
}
|
}
|
||||||
Value out = make_array_from_values(pairs, n);
|
Value out = make_array_from_values(pairs, n);
|
||||||
for (int i = 0; i < n; ++i) free_value(pairs[i]);
|
for (int i = 0; i < n; ++i)
|
||||||
|
free_value(pairs[i]);
|
||||||
free(pairs);
|
free(pairs);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
@ -39,7 +40,7 @@ Value bi_zip(const Value *a, const Value *b) {
|
||||||
int nb = array_length(b);
|
int nb = array_length(b);
|
||||||
int n = na < nb ? na : nb;
|
int n = na < nb ? na : nb;
|
||||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||||
Value *pairs = (Value*)malloc(sizeof(Value) * n);
|
Value *pairs = (Value *)malloc(sizeof(Value) * n);
|
||||||
if (!pairs) return make_array_from_values(NULL, 0);
|
if (!pairs) return make_array_from_values(NULL, 0);
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
Value av, bv;
|
Value av, bv;
|
||||||
|
|
@ -54,7 +55,8 @@ Value bi_zip(const Value *a, const Value *b) {
|
||||||
pairs[i] = kv;
|
pairs[i] = kv;
|
||||||
}
|
}
|
||||||
Value out = make_array_from_values(pairs, n);
|
Value out = make_array_from_values(pairs, n);
|
||||||
for (int i = 0; i < n; ++i) free_value(pairs[i]);
|
for (int i = 0; i < n; ++i)
|
||||||
|
free_value(pairs[i]);
|
||||||
free(pairs);
|
free(pairs);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
src/map.c
41
src/map.c
|
|
@ -21,7 +21,7 @@ typedef struct Map {
|
||||||
} Map;
|
} Map;
|
||||||
|
|
||||||
Value make_map_empty(void) {
|
Value make_map_empty(void) {
|
||||||
Map *m = (Map*)malloc(sizeof(Map));
|
Map *m = (Map *)malloc(sizeof(Map));
|
||||||
if (!m) return make_nil();
|
if (!m) return make_nil();
|
||||||
m->refcount = 1;
|
m->refcount = 1;
|
||||||
m->count = 0;
|
m->count = 0;
|
||||||
|
|
@ -30,16 +30,17 @@ Value make_map_empty(void) {
|
||||||
m->vals = NULL;
|
m->vals = NULL;
|
||||||
Value v;
|
Value v;
|
||||||
v.type = VAL_MAP;
|
v.type = VAL_MAP;
|
||||||
v.map = (struct Map*)m;
|
v.map = (struct Map *)m;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int map_ensure_cap(Map *m, int need) {
|
static int map_ensure_cap(Map *m, int need) {
|
||||||
if (m->cap >= need) return 1;
|
if (m->cap >= need) return 1;
|
||||||
int ncap = m->cap == 0 ? 4 : m->cap * 2;
|
int ncap = m->cap == 0 ? 4 : m->cap * 2;
|
||||||
while (ncap < need) ncap *= 2;
|
while (ncap < need)
|
||||||
char **nkeys = (char**)realloc(m->keys, sizeof(char*) * ncap);
|
ncap *= 2;
|
||||||
Value *nvals = (Value*)realloc(m->vals, sizeof(Value) * ncap);
|
char **nkeys = (char **)realloc(m->keys, sizeof(char *) * ncap);
|
||||||
|
Value *nvals = (Value *)realloc(m->vals, sizeof(Value) * ncap);
|
||||||
if (!nkeys || !nvals) return 0;
|
if (!nkeys || !nvals) return 0;
|
||||||
m->keys = nkeys;
|
m->keys = nkeys;
|
||||||
m->vals = nvals;
|
m->vals = nvals;
|
||||||
|
|
@ -48,8 +49,11 @@ static int map_ensure_cap(Map *m, int need) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int map_set(Value *vm, const char *key, Value v) {
|
int map_set(Value *vm, const char *key, Value v) {
|
||||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) { free_value(v); return 0; }
|
if (!vm || vm->type != VAL_MAP || !vm->map || !key) {
|
||||||
Map *m = (Map*)vm->map;
|
free_value(v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Map *m = (Map *)vm->map;
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
if (strcmp(m->keys[i], key) == 0) {
|
if (strcmp(m->keys[i], key) == 0) {
|
||||||
free_value(m->vals[i]);
|
free_value(m->vals[i]);
|
||||||
|
|
@ -57,7 +61,10 @@ int map_set(Value *vm, const char *key, Value v) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!map_ensure_cap(m, m->count + 1)) { free_value(v); return 0; }
|
if (!map_ensure_cap(m, m->count + 1)) {
|
||||||
|
free_value(v);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
m->keys[m->count] = strdup(key);
|
m->keys[m->count] = strdup(key);
|
||||||
m->vals[m->count] = v;
|
m->vals[m->count] = v;
|
||||||
m->count++;
|
m->count++;
|
||||||
|
|
@ -66,7 +73,7 @@ int map_set(Value *vm, const char *key, Value v) {
|
||||||
|
|
||||||
int map_get_copy(const Value *vm, const char *key, Value *out) {
|
int map_get_copy(const Value *vm, const char *key, Value *out) {
|
||||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
||||||
Map *m = (Map*)vm->map;
|
Map *m = (Map *)vm->map;
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
if (strcmp(m->keys[i], key) == 0) {
|
if (strcmp(m->keys[i], key) == 0) {
|
||||||
if (out) *out = copy_value(&m->vals[i]);
|
if (out) *out = copy_value(&m->vals[i]);
|
||||||
|
|
@ -78,7 +85,7 @@ int map_get_copy(const Value *vm, const char *key, Value *out) {
|
||||||
|
|
||||||
int map_has(const Value *vm, const char *key) {
|
int map_has(const Value *vm, const char *key) {
|
||||||
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
if (!vm || vm->type != VAL_MAP || !vm->map || !key) return 0;
|
||||||
Map *m = (Map*)vm->map;
|
Map *m = (Map *)vm->map;
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
if (strcmp(m->keys[i], key) == 0) return 1;
|
if (strcmp(m->keys[i], key) == 0) return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -87,30 +94,32 @@ int map_has(const Value *vm, const char *key) {
|
||||||
|
|
||||||
Value map_keys_array(const Value *vm) {
|
Value map_keys_array(const Value *vm) {
|
||||||
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
||||||
Map *m = (Map*)vm->map;
|
Map *m = (Map *)vm->map;
|
||||||
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
||||||
Value *tmp = (Value*)malloc(sizeof(Value) * m->count);
|
Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
|
||||||
if (!tmp) return make_array_from_values(NULL, 0);
|
if (!tmp) return make_array_from_values(NULL, 0);
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
tmp[i] = make_string(m->keys[i]);
|
tmp[i] = make_string(m->keys[i]);
|
||||||
}
|
}
|
||||||
Value arr = make_array_from_values(tmp, m->count);
|
Value arr = make_array_from_values(tmp, m->count);
|
||||||
for (int i = 0; i < m->count; ++i) free_value(tmp[i]);
|
for (int i = 0; i < m->count; ++i)
|
||||||
|
free_value(tmp[i]);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value map_values_array(const Value *vm) {
|
Value map_values_array(const Value *vm) {
|
||||||
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
if (!vm || vm->type != VAL_MAP || !vm->map) return make_array_from_values(NULL, 0);
|
||||||
Map *m = (Map*)vm->map;
|
Map *m = (Map *)vm->map;
|
||||||
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
if (m->count <= 0) return make_array_from_values(NULL, 0);
|
||||||
Value *tmp = (Value*)malloc(sizeof(Value) * m->count);
|
Value *tmp = (Value *)malloc(sizeof(Value) * m->count);
|
||||||
if (!tmp) return make_array_from_values(NULL, 0);
|
if (!tmp) return make_array_from_values(NULL, 0);
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
tmp[i] = copy_value(&m->vals[i]);
|
tmp[i] = copy_value(&m->vals[i]);
|
||||||
}
|
}
|
||||||
Value arr = make_array_from_values(tmp, m->count);
|
Value arr = make_array_from_values(tmp, m->count);
|
||||||
for (int i = 0; i < m->count; ++i) free_value(tmp[i]);
|
for (int i = 0; i < m->count; ++i)
|
||||||
|
free_value(tmp[i]);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3417
src/parser.c
3417
src/parser.c
File diff suppressed because it is too large
Load diff
|
|
@ -7,21 +7,30 @@
|
||||||
* https://opensource.org/license/apache-2-0
|
* https://opensource.org/license/apache-2-0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include "parser.h"
|
|
||||||
|
|
||||||
static char *read_file_all(const char *path, size_t *out_len) {
|
static char *read_file_all(const char *path, size_t *out_len) {
|
||||||
FILE *f = fopen(path, "rb");
|
FILE *f = fopen(path, "rb");
|
||||||
if (!f) return NULL;
|
if (!f) return NULL;
|
||||||
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); return NULL; }
|
if (fseek(f, 0, SEEK_END) != 0) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
long sz = ftell(f);
|
long sz = ftell(f);
|
||||||
if (sz < 0) { fclose(f); return NULL; }
|
if (sz < 0) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
rewind(f);
|
rewind(f);
|
||||||
char *buf = (char*)malloc((size_t)sz + 1);
|
char *buf = (char *)malloc((size_t)sz + 1);
|
||||||
if (!buf) { fclose(f); return NULL; }
|
if (!buf) {
|
||||||
|
fclose(f);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
size_t n = fread(buf, 1, (size_t)sz, f);
|
size_t n = fread(buf, 1, (size_t)sz, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
buf[n] = '\0';
|
buf[n] = '\0';
|
||||||
|
|
@ -32,13 +41,17 @@ static char *read_file_all(const char *path, size_t *out_len) {
|
||||||
static void skip_ws(const char *src, size_t len, size_t *pos) {
|
static void skip_ws(const char *src, size_t len, size_t *pos) {
|
||||||
while (*pos < len) {
|
while (*pos < len) {
|
||||||
char c = src[*pos];
|
char c = src[*pos];
|
||||||
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { (*pos)++; continue; }
|
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
|
||||||
|
(*pos)++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void skip_line(const char *src, size_t len, size_t *pos) {
|
static void skip_line(const char *src, size_t len, size_t *pos) {
|
||||||
while (*pos < len && src[*pos] != '\n') (*pos)++;
|
while (*pos < len && src[*pos] != '\n')
|
||||||
|
(*pos)++;
|
||||||
if (*pos < len && src[*pos] == '\n') (*pos)++;
|
if (*pos < len && src[*pos] == '\n') (*pos)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +65,8 @@ static void skip_comments(const char *src, size_t len, size_t *pos) {
|
||||||
}
|
}
|
||||||
if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '*') {
|
if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '*') {
|
||||||
*pos += 2;
|
*pos += 2;
|
||||||
while (*pos + 1 < len && !(src[*pos] == '*' && src[*pos + 1] == '/')) (*pos)++;
|
while (*pos + 1 < len && !(src[*pos] == '*' && src[*pos + 1] == '/'))
|
||||||
|
(*pos)++;
|
||||||
if (*pos + 1 < len) *pos += 2;
|
if (*pos + 1 < len) *pos += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -76,14 +90,18 @@ static void skip_identifier(const char *src, size_t len, size_t *pos) {
|
||||||
size_t p = *pos;
|
size_t p = *pos;
|
||||||
if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) {
|
if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) {
|
||||||
p++;
|
p++;
|
||||||
while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_')) p++;
|
while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_'))
|
||||||
|
p++;
|
||||||
}
|
}
|
||||||
*pos = p;
|
*pos = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int consume_char(const char *src, size_t len, size_t *pos, char expected) {
|
static int consume_char(const char *src, size_t len, size_t *pos, char expected) {
|
||||||
skip_ws(src, len, pos);
|
skip_ws(src, len, pos);
|
||||||
if (*pos < len && src[*pos] == expected) { (*pos)++; return 1; }
|
if (*pos < len && src[*pos] == expected) {
|
||||||
|
(*pos)++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,37 +112,60 @@ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t
|
||||||
if (quote != '"' && quote != '\'') return NULL;
|
if (quote != '"' && quote != '\'') return NULL;
|
||||||
(*pos)++; // skip opening quote
|
(*pos)++; // skip opening quote
|
||||||
size_t cap = 64, out_len = 0;
|
size_t cap = 64, out_len = 0;
|
||||||
char *out = (char*)malloc(cap);
|
char *out = (char *)malloc(cap);
|
||||||
if (!out) return NULL;
|
if (!out) return NULL;
|
||||||
while (*pos < len) {
|
while (*pos < len) {
|
||||||
char c = src[*pos];
|
char c = src[*pos];
|
||||||
if (c == quote) { (*pos)++; break; }
|
if (c == quote) {
|
||||||
|
(*pos)++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (c == '\\') {
|
if (c == '\\') {
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
if (*pos >= len) break;
|
if (*pos >= len) break;
|
||||||
char e = src[*pos];
|
char e = src[*pos];
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case 'n': c = '\n'; break;
|
case 'n':
|
||||||
case 'r': c = '\r'; break;
|
c = '\n';
|
||||||
case 't': c = '\t'; break;
|
break;
|
||||||
case '\\': c = '\\'; break;
|
case 'r':
|
||||||
case '"': c = '"'; break;
|
c = '\r';
|
||||||
case '\'': c = '\''; break;
|
break;
|
||||||
default: c = e; break;
|
case 't':
|
||||||
|
c = '\t';
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
c = '\\';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
c = '"';
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
c = '\'';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
c = e;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out_len + 1 >= cap) {
|
if (out_len + 1 >= cap) {
|
||||||
cap *= 2;
|
cap *= 2;
|
||||||
char *tmp = (char*)realloc(out, cap);
|
char *tmp = (char *)realloc(out, cap);
|
||||||
if (!tmp) { free(out); return NULL; }
|
if (!tmp) {
|
||||||
|
free(out);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
out = tmp;
|
out = tmp;
|
||||||
}
|
}
|
||||||
out[out_len++] = c;
|
out[out_len++] = c;
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
}
|
}
|
||||||
if (out_len + 1 >= cap) {
|
if (out_len + 1 >= cap) {
|
||||||
char *tmp = (char*)realloc(out, cap + 1);
|
char *tmp = (char *)realloc(out, cap + 1);
|
||||||
if (!tmp) { free(out); return NULL; }
|
if (!tmp) {
|
||||||
|
free(out);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
out = tmp;
|
out = tmp;
|
||||||
}
|
}
|
||||||
out[out_len] = '\0';
|
out[out_len] = '\0';
|
||||||
|
|
@ -136,7 +177,10 @@ static char *parse_string_literal_any_quote(const char *src, size_t len, size_t
|
||||||
static void skip_spaces(const char *src, size_t len, size_t *pos) {
|
static void skip_spaces(const char *src, size_t len, size_t *pos) {
|
||||||
while (*pos < len) {
|
while (*pos < len) {
|
||||||
char c = src[*pos];
|
char c = src[*pos];
|
||||||
if (c == ' ' || c == '\t' || c == '\r') { (*pos)++; continue; }
|
if (c == ' ' || c == '\t' || c == '\r') {
|
||||||
|
(*pos)++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -146,9 +190,10 @@ static int read_identifier_into(const char *src, size_t len, size_t *pos, char *
|
||||||
if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) {
|
if (p < len && (isalpha((unsigned char)src[p]) || src[p] == '_')) {
|
||||||
size_t start = p;
|
size_t start = p;
|
||||||
p++;
|
p++;
|
||||||
while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_')) p++;
|
while (p < len && (isalnum((unsigned char)src[p]) || src[p] == '_'))
|
||||||
|
p++;
|
||||||
size_t n = p - start;
|
size_t n = p - start;
|
||||||
char *name = (char*)malloc(n + 1);
|
char *name = (char *)malloc(n + 1);
|
||||||
if (!name) return 0;
|
if (!name) return 0;
|
||||||
memcpy(name, src + start, n);
|
memcpy(name, src + start, n);
|
||||||
name[n] = '\0';
|
name[n] = '\0';
|
||||||
|
|
@ -167,12 +212,18 @@ static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos
|
||||||
if (src[p] == '-') sign = -1;
|
if (src[p] == '-') sign = -1;
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
if (p >= len) { *ok = 0; return 0; }
|
if (p >= len) {
|
||||||
|
*ok = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Hexadecimal: 0x... or 0X... */
|
/* Hexadecimal: 0x... or 0X... */
|
||||||
if ((p + 1) < len && src[p] == '0' && (src[p + 1] == 'x' || src[p + 1] == 'X')) {
|
if ((p + 1) < len && src[p] == '0' && (src[p + 1] == 'x' || src[p + 1] == 'X')) {
|
||||||
p += 2;
|
p += 2;
|
||||||
if (p >= len || !isxdigit((unsigned char)src[p])) { *ok = 0; return 0; }
|
if (p >= len || !isxdigit((unsigned char)src[p])) {
|
||||||
|
*ok = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
uint64_t val = 0;
|
uint64_t val = 0;
|
||||||
while (p < len && isxdigit((unsigned char)src[p])) {
|
while (p < len && isxdigit((unsigned char)src[p])) {
|
||||||
char c = src[p];
|
char c = src[p];
|
||||||
|
|
@ -189,7 +240,10 @@ static uint64_t parse_int_literal_value(const char *src, size_t len, size_t *pos
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decimal fallback */
|
/* Decimal fallback */
|
||||||
if (!isdigit((unsigned char)src[p])) { *ok = 0; return 0; }
|
if (!isdigit((unsigned char)src[p])) {
|
||||||
|
*ok = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
uint64_t val = 0;
|
uint64_t val = 0;
|
||||||
while (p < len && isdigit((unsigned char)src[p])) {
|
while (p < len && isdigit((unsigned char)src[p])) {
|
||||||
val = val * 10 + (uint64_t)(src[p] - '0');
|
val = val * 10 + (uint64_t)(src[p] - '0');
|
||||||
|
|
@ -220,7 +274,7 @@ typedef struct {
|
||||||
} StrBuf;
|
} StrBuf;
|
||||||
|
|
||||||
static void sb_init(StrBuf *sb) {
|
static void sb_init(StrBuf *sb) {
|
||||||
sb->buf = (char*)malloc(256);
|
sb->buf = (char *)malloc(256);
|
||||||
sb->cap = sb->buf ? 256 : 0;
|
sb->cap = sb->buf ? 256 : 0;
|
||||||
sb->len = 0;
|
sb->len = 0;
|
||||||
if (sb->buf) sb->buf[0] = '\0';
|
if (sb->buf) sb->buf[0] = '\0';
|
||||||
|
|
@ -229,8 +283,9 @@ static void sb_init(StrBuf *sb) {
|
||||||
static void sb_reserve(StrBuf *sb, size_t need) {
|
static void sb_reserve(StrBuf *sb, size_t need) {
|
||||||
if (need <= sb->cap) return;
|
if (need <= sb->cap) return;
|
||||||
size_t nc = sb->cap ? sb->cap : 256;
|
size_t nc = sb->cap ? sb->cap : 256;
|
||||||
while (nc < need) nc *= 2;
|
while (nc < need)
|
||||||
char *nb = (char*)xrealloc(sb->buf, nc);
|
nc *= 2;
|
||||||
|
char *nb = (char *)xrealloc(sb->buf, nc);
|
||||||
if (!nb) return;
|
if (!nb) return;
|
||||||
sb->buf = nb;
|
sb->buf = nb;
|
||||||
sb->cap = nc;
|
sb->cap = nc;
|
||||||
|
|
@ -273,7 +328,7 @@ static void nl_add(NameList *nl, const char *name) {
|
||||||
if (!name || !name[0]) return;
|
if (!name || !name[0]) return;
|
||||||
if (nl->count >= nl->cap) {
|
if (nl->count >= nl->cap) {
|
||||||
int ncap = nl->cap ? nl->cap * 2 : 8;
|
int ncap = nl->cap ? nl->cap * 2 : 8;
|
||||||
char **nn = (char**)realloc(nl->names, (size_t)ncap * sizeof(char*));
|
char **nn = (char **)realloc(nl->names, (size_t)ncap * sizeof(char *));
|
||||||
if (!nn) return;
|
if (!nn) return;
|
||||||
nl->names = nn;
|
nl->names = nn;
|
||||||
nl->cap = ncap;
|
nl->cap = ncap;
|
||||||
|
|
@ -283,7 +338,8 @@ static void nl_add(NameList *nl, const char *name) {
|
||||||
|
|
||||||
static void nl_free(NameList *nl) {
|
static void nl_free(NameList *nl) {
|
||||||
if (!nl) return;
|
if (!nl) return;
|
||||||
for (int i = 0; i < nl->count; ++i) free(nl->names[i]);
|
for (int i = 0; i < nl->count; ++i)
|
||||||
|
free(nl->names[i]);
|
||||||
free(nl->names);
|
free(nl->names);
|
||||||
nl->names = NULL;
|
nl->names = NULL;
|
||||||
nl->count = nl->cap = 0;
|
nl->count = nl->cap = 0;
|
||||||
|
|
@ -296,12 +352,16 @@ static void collect_exports_top_level(const char *text, NameList *out) {
|
||||||
size_t len = strlen(text);
|
size_t len = strlen(text);
|
||||||
int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0;
|
int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0;
|
||||||
int bol = 1;
|
int bol = 1;
|
||||||
for (size_t i = 0; i < len; ) {
|
for (size_t i = 0; i < len;) {
|
||||||
char c = text[i];
|
char c = text[i];
|
||||||
|
|
||||||
if (in_line) {
|
if (in_line) {
|
||||||
if (c == '\n') { in_line = 0; bol = 1; }
|
if (c == '\n') {
|
||||||
else { bol = 0; }
|
in_line = 0;
|
||||||
|
bol = 1;
|
||||||
|
} else {
|
||||||
|
bol = 0;
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -317,16 +377,30 @@ static void collect_exports_top_level(const char *text, NameList *out) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (in_sq) {
|
if (in_sq) {
|
||||||
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; }
|
if (!esc && c == '\\') {
|
||||||
if (!esc && c == '\'') { in_sq = 0; }
|
esc = 1;
|
||||||
|
i++;
|
||||||
|
bol = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!esc && c == '\'') {
|
||||||
|
in_sq = 0;
|
||||||
|
}
|
||||||
esc = 0;
|
esc = 0;
|
||||||
bol = (c == '\n');
|
bol = (c == '\n');
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (in_dq) {
|
if (in_dq) {
|
||||||
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; }
|
if (!esc && c == '\\') {
|
||||||
if (!esc && c == '"') { in_dq = 0; }
|
esc = 1;
|
||||||
|
i++;
|
||||||
|
bol = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!esc && c == '"') {
|
||||||
|
in_dq = 0;
|
||||||
|
}
|
||||||
esc = 0;
|
esc = 0;
|
||||||
bol = (c == '\n');
|
bol = (c == '\n');
|
||||||
i++;
|
i++;
|
||||||
|
|
@ -345,14 +419,27 @@ static void collect_exports_top_level(const char *text, NameList *out) {
|
||||||
i += 2;
|
i += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c == '\'') { in_sq = 1; bol = 0; i++; continue; }
|
if (c == '\'') {
|
||||||
if (c == '"') { in_dq = 1; bol = 0; i++; continue; }
|
in_sq = 1;
|
||||||
|
bol = 0;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == '"') {
|
||||||
|
in_dq = 1;
|
||||||
|
bol = 0;
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (bol) {
|
if (bol) {
|
||||||
/* Compute leading spaces to filter out indented constructs */
|
/* Compute leading spaces to filter out indented constructs */
|
||||||
size_t j = i;
|
size_t j = i;
|
||||||
int spaces = 0;
|
int spaces = 0;
|
||||||
while (j < len && text[j] == ' ') { spaces++; j++; }
|
while (j < len && text[j] == ' ') {
|
||||||
|
spaces++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
if (j < len && text[j] == '\t') {
|
if (j < len && text[j] == '\t') {
|
||||||
/* tabs not allowed for indentation; treat as not top-level */
|
/* tabs not allowed for indentation; treat as not top-level */
|
||||||
bol = 0;
|
bol = 0;
|
||||||
|
|
@ -366,12 +453,14 @@ static void collect_exports_top_level(const char *text, NameList *out) {
|
||||||
const char *kw2 = "class";
|
const char *kw2 = "class";
|
||||||
if (j + 3 <= len && strncmp(text + j, kw1, 3) == 0 && (j + 3 == len || isspace((unsigned char)text[j + 3]))) {
|
if (j + 3 <= len && strncmp(text + j, kw1, 3) == 0 && (j + 3 == len || isspace((unsigned char)text[j + 3]))) {
|
||||||
size_t p = j + 3;
|
size_t p = j + 3;
|
||||||
while (p < len && (text[p] == ' ' || text[p] == '\t')) p++;
|
while (p < len && (text[p] == ' ' || text[p] == '\t'))
|
||||||
|
p++;
|
||||||
/* read identifier */
|
/* read identifier */
|
||||||
size_t start = p;
|
size_t start = p;
|
||||||
if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) {
|
if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) {
|
||||||
p++;
|
p++;
|
||||||
while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_')) p++;
|
while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_'))
|
||||||
|
p++;
|
||||||
size_t n = p - start;
|
size_t n = p - start;
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
char tmp[256];
|
char tmp[256];
|
||||||
|
|
@ -383,12 +472,14 @@ static void collect_exports_top_level(const char *text, NameList *out) {
|
||||||
}
|
}
|
||||||
} else if (j + 5 <= len && strncmp(text + j, kw2, 5) == 0 && (j + 5 == len || isspace((unsigned char)text[j + 5]))) {
|
} else if (j + 5 <= len && strncmp(text + j, kw2, 5) == 0 && (j + 5 == len || isspace((unsigned char)text[j + 5]))) {
|
||||||
size_t p = j + 5;
|
size_t p = j + 5;
|
||||||
while (p < len && (text[p] == ' ' || text[p] == '\t')) p++;
|
while (p < len && (text[p] == ' ' || text[p] == '\t'))
|
||||||
|
p++;
|
||||||
/* read identifier */
|
/* read identifier */
|
||||||
size_t start = p;
|
size_t start = p;
|
||||||
if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) {
|
if (p < len && (isalpha((unsigned char)text[p]) || text[p] == '_')) {
|
||||||
p++;
|
p++;
|
||||||
while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_')) p++;
|
while (p < len && (isalnum((unsigned char)text[p]) || text[p] == '_'))
|
||||||
|
p++;
|
||||||
size_t n = p - start;
|
size_t n = p - start;
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
char tmp[256];
|
char tmp[256];
|
||||||
|
|
@ -415,10 +506,10 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
return strdup("");
|
return strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Build-time default, can be overridden by compiler define -DDEFAULT_LIB_DIR=".../" */
|
/* Build-time default, can be overridden by compiler define -DDEFAULT_LIB_DIR=".../" */
|
||||||
#ifndef DEFAULT_LIB_DIR
|
#ifndef DEFAULT_LIB_DIR
|
||||||
#define DEFAULT_LIB_DIR "/usr/share/fun/lib/"
|
#define DEFAULT_LIB_DIR "/usr/share/fun/lib/"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char *env_lib = getenv("FUN_LIB_DIR");
|
const char *env_lib = getenv("FUN_LIB_DIR");
|
||||||
size_t len = strlen(src);
|
size_t len = strlen(src);
|
||||||
|
|
@ -427,14 +518,15 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0;
|
int in_line = 0, in_block = 0, in_sq = 0, in_dq = 0, esc = 0;
|
||||||
int bol = 1; /* beginning of line */
|
int bol = 1; /* beginning of line */
|
||||||
|
|
||||||
for (size_t i = 0; i < len; ) {
|
for (size_t i = 0; i < len;) {
|
||||||
char c = src[i];
|
char c = src[i];
|
||||||
|
|
||||||
/* Detect include directive at BOL, outside comments/strings */
|
/* Detect include directive at BOL, outside comments/strings */
|
||||||
if (bol && !in_block && !in_sq && !in_dq) {
|
if (bol && !in_block && !in_sq && !in_dq) {
|
||||||
size_t j = i;
|
size_t j = i;
|
||||||
/* skip leading spaces/tabs */
|
/* skip leading spaces/tabs */
|
||||||
while (j < len && (src[j] == ' ' || src[j] == '\t')) j++;
|
while (j < len && (src[j] == ' ' || src[j] == '\t'))
|
||||||
|
j++;
|
||||||
size_t k = j;
|
size_t k = j;
|
||||||
if (k < len && src[k] == '#') k++;
|
if (k < len && src[k] == '#') k++;
|
||||||
const char *kw = "include";
|
const char *kw = "include";
|
||||||
|
|
@ -442,36 +534,42 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
if (k + kwlen <= len && strncmp(src + k, kw, kwlen) == 0) {
|
if (k + kwlen <= len && strncmp(src + k, kw, kwlen) == 0) {
|
||||||
k += kwlen;
|
k += kwlen;
|
||||||
/* next must be space/tab or delimiter */
|
/* next must be space/tab or delimiter */
|
||||||
while (k < len && (src[k] == ' ' || src[k] == '\t')) k++;
|
while (k < len && (src[k] == ' ' || src[k] == '\t'))
|
||||||
|
k++;
|
||||||
if (k < len && (src[k] == '"' || src[k] == '<')) {
|
if (k < len && (src[k] == '"' || src[k] == '<')) {
|
||||||
char opener = src[k];
|
char opener = src[k];
|
||||||
char closer = (opener == '"') ? '"' : '>';
|
char closer = (opener == '"') ? '"' : '>';
|
||||||
k++;
|
k++;
|
||||||
size_t path_start = k;
|
size_t path_start = k;
|
||||||
while (k < len && src[k] != closer) k++;
|
while (k < len && src[k] != closer)
|
||||||
|
k++;
|
||||||
if (k < len && src[k] == closer) {
|
if (k < len && src[k] == closer) {
|
||||||
size_t path_len = k - path_start;
|
size_t path_len = k - path_start;
|
||||||
char *path = (char*)malloc(path_len + 1);
|
char *path = (char *)malloc(path_len + 1);
|
||||||
if (path) {
|
if (path) {
|
||||||
memcpy(path, src + path_start, path_len);
|
memcpy(path, src + path_start, path_len);
|
||||||
path[path_len] = '\0';
|
path[path_len] = '\0';
|
||||||
|
|
||||||
/* parse optional 'as <alias>' then advance to end of line */
|
/* parse optional 'as <alias>' then advance to end of line */
|
||||||
k++;
|
k++;
|
||||||
char ns[64]; ns[0] = '\0';
|
char ns[64];
|
||||||
|
ns[0] = '\0';
|
||||||
/* skip spaces/tabs */
|
/* skip spaces/tabs */
|
||||||
size_t ap = k;
|
size_t ap = k;
|
||||||
while (ap < len && (src[ap] == ' ' || src[ap] == '\t')) ap++;
|
while (ap < len && (src[ap] == ' ' || src[ap] == '\t'))
|
||||||
|
ap++;
|
||||||
/* optional 'as' */
|
/* optional 'as' */
|
||||||
const char *askw = "as";
|
const char *askw = "as";
|
||||||
if (ap + 2 <= len && strncmp(src + ap, askw, 2) == 0 && (ap + 2 == len || isspace((unsigned char)src[ap + 2]))) {
|
if (ap + 2 <= len && strncmp(src + ap, askw, 2) == 0 && (ap + 2 == len || isspace((unsigned char)src[ap + 2]))) {
|
||||||
ap += 2;
|
ap += 2;
|
||||||
while (ap < len && (src[ap] == ' ' || src[ap] == '\t')) ap++;
|
while (ap < len && (src[ap] == ' ' || src[ap] == '\t'))
|
||||||
|
ap++;
|
||||||
/* read identifier [A-Za-z_][A-Za-z0-9_]* */
|
/* read identifier [A-Za-z_][A-Za-z0-9_]* */
|
||||||
size_t start = ap;
|
size_t start = ap;
|
||||||
if (ap < len && (isalpha((unsigned char)src[ap]) || src[ap] == '_')) {
|
if (ap < len && (isalpha((unsigned char)src[ap]) || src[ap] == '_')) {
|
||||||
ap++;
|
ap++;
|
||||||
while (ap < len && (isalnum((unsigned char)src[ap]) || src[ap] == '_')) ap++;
|
while (ap < len && (isalnum((unsigned char)src[ap]) || src[ap] == '_'))
|
||||||
|
ap++;
|
||||||
size_t n = ap - start;
|
size_t n = ap - start;
|
||||||
size_t copy = (n < sizeof(ns) - 1) ? n : (sizeof(ns) - 1);
|
size_t copy = (n < sizeof(ns) - 1) ? n : (sizeof(ns) - 1);
|
||||||
memcpy(ns, src + start, copy);
|
memcpy(ns, src + start, copy);
|
||||||
|
|
@ -481,7 +579,8 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
}
|
}
|
||||||
/* advance to end of line */
|
/* advance to end of line */
|
||||||
k = ap;
|
k = ap;
|
||||||
while (k < len && src[k] != '\n') k++;
|
while (k < len && src[k] != '\n')
|
||||||
|
k++;
|
||||||
if (k < len && src[k] == '\n') k++;
|
if (k < len && src[k] == '\n') k++;
|
||||||
|
|
||||||
/* resolve file path; for <...> try FUN_LIB_DIR first, then default */
|
/* resolve file path; for <...> try FUN_LIB_DIR first, then default */
|
||||||
|
|
@ -547,9 +646,14 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
if (startp[0] == '#' && startp[1] == '!') {
|
if (startp[0] == '#' && startp[1] == '!') {
|
||||||
/* skip until end of line, handling CR, LF, CRLF */
|
/* skip until end of line, handling CR, LF, CRLF */
|
||||||
const char *q = startp;
|
const char *q = startp;
|
||||||
while (*q && *q != '\n' && *q != '\r') q++;
|
while (*q && *q != '\n' && *q != '\r')
|
||||||
if (*q == '\r') { q++; if (*q == '\n') q++; }
|
q++;
|
||||||
else if (*q == '\n') { q++; }
|
if (*q == '\r') {
|
||||||
|
q++;
|
||||||
|
if (*q == '\n') q++;
|
||||||
|
} else if (*q == '\n') {
|
||||||
|
q++;
|
||||||
|
}
|
||||||
startp = q;
|
startp = q;
|
||||||
}
|
}
|
||||||
char *inc_clean = strdup(startp);
|
char *inc_clean = strdup(startp);
|
||||||
|
|
@ -584,7 +688,8 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
|
|
||||||
/* If alias is present, export top-level fun/class into alias map */
|
/* If alias is present, export top-level fun/class into alias map */
|
||||||
if (ns[0] != '\0') {
|
if (ns[0] != '\0') {
|
||||||
NameList nl; nl_init(&nl);
|
NameList nl;
|
||||||
|
nl_init(&nl);
|
||||||
collect_exports_top_level(exp, &nl);
|
collect_exports_top_level(exp, &nl);
|
||||||
for (int ei = 0; ei < nl.count; ++ei) {
|
for (int ei = 0; ei < nl.count; ++ei) {
|
||||||
sb_append(&out, ns);
|
sb_append(&out, ns);
|
||||||
|
|
@ -614,7 +719,12 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
/* normal stateful copy with comment/string tracking */
|
/* normal stateful copy with comment/string tracking */
|
||||||
if (in_line) {
|
if (in_line) {
|
||||||
sb_append_ch(&out, c);
|
sb_append_ch(&out, c);
|
||||||
if (c == '\n') { in_line = 0; bol = 1; } else { bol = 0; }
|
if (c == '\n') {
|
||||||
|
in_line = 0;
|
||||||
|
bol = 1;
|
||||||
|
} else {
|
||||||
|
bol = 0;
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -633,8 +743,15 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
}
|
}
|
||||||
if (in_sq) {
|
if (in_sq) {
|
||||||
sb_append_ch(&out, c);
|
sb_append_ch(&out, c);
|
||||||
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; }
|
if (!esc && c == '\\') {
|
||||||
if (!esc && c == '\'') { in_sq = 0; }
|
esc = 1;
|
||||||
|
i++;
|
||||||
|
bol = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!esc && c == '\'') {
|
||||||
|
in_sq = 0;
|
||||||
|
}
|
||||||
esc = 0;
|
esc = 0;
|
||||||
bol = (c == '\n') ? 1 : 0;
|
bol = (c == '\n') ? 1 : 0;
|
||||||
i++;
|
i++;
|
||||||
|
|
@ -642,8 +759,15 @@ static char *preprocess_includes_internal(const char *src, int depth) {
|
||||||
}
|
}
|
||||||
if (in_dq) {
|
if (in_dq) {
|
||||||
sb_append_ch(&out, c);
|
sb_append_ch(&out, c);
|
||||||
if (!esc && c == '\\') { esc = 1; i++; bol = 0; continue; }
|
if (!esc && c == '\\') {
|
||||||
if (!esc && c == '"') { in_dq = 0; }
|
esc = 1;
|
||||||
|
i++;
|
||||||
|
bol = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!esc && c == '"') {
|
||||||
|
in_dq = 0;
|
||||||
|
}
|
||||||
esc = 0;
|
esc = 0;
|
||||||
bol = (c == '\n') ? 1 : 0;
|
bol = (c == '\n') ? 1 : 0;
|
||||||
i++;
|
i++;
|
||||||
|
|
@ -698,7 +822,6 @@ char *preprocess_includes(const char *src) {
|
||||||
return preprocess_includes_internal(src, 0);
|
return preprocess_includes_internal(src, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Float literal parser: supports decimal and scientific notation. Returns parsed double and advances pos on success. */
|
/* Float literal parser: supports decimal and scientific notation. Returns parsed double and advances pos on success. */
|
||||||
static double parse_float_literal_value(const char *src, size_t len, size_t *pos, int *ok) {
|
static double parse_float_literal_value(const char *src, size_t len, size_t *pos, int *ok) {
|
||||||
size_t p = *pos;
|
size_t p = *pos;
|
||||||
|
|
@ -712,13 +835,19 @@ static double parse_float_literal_value(const char *src, size_t len, size_t *pos
|
||||||
if (p < len && (src[p] == '+' || src[p] == '-')) p++;
|
if (p < len && (src[p] == '+' || src[p] == '-')) p++;
|
||||||
|
|
||||||
/* integer part */
|
/* integer part */
|
||||||
while (p < len && isdigit((unsigned char)src[p])) { p++; saw_digit = 1; }
|
while (p < len && isdigit((unsigned char)src[p])) {
|
||||||
|
p++;
|
||||||
|
saw_digit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* fractional part */
|
/* fractional part */
|
||||||
if (p < len && src[p] == '.') {
|
if (p < len && src[p] == '.') {
|
||||||
saw_dot = 1;
|
saw_dot = 1;
|
||||||
p++;
|
p++;
|
||||||
while (p < len && isdigit((unsigned char)src[p])) { p++; saw_digit = 1; }
|
while (p < len && isdigit((unsigned char)src[p])) {
|
||||||
|
p++;
|
||||||
|
saw_digit = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* exponent part */
|
/* exponent part */
|
||||||
|
|
@ -727,28 +856,39 @@ static double parse_float_literal_value(const char *src, size_t len, size_t *pos
|
||||||
size_t epos = p + 1;
|
size_t epos = p + 1;
|
||||||
if (epos < len && (src[epos] == '+' || src[epos] == '-')) epos++;
|
if (epos < len && (src[epos] == '+' || src[epos] == '-')) epos++;
|
||||||
size_t digits_start = epos;
|
size_t digits_start = epos;
|
||||||
while (epos < len && isdigit((unsigned char)src[epos])) { epos++; }
|
while (epos < len && isdigit((unsigned char)src[epos])) {
|
||||||
|
epos++;
|
||||||
|
}
|
||||||
if (epos == digits_start) {
|
if (epos == digits_start) {
|
||||||
/* no digits after exponent -> not a float */
|
/* no digits after exponent -> not a float */
|
||||||
*ok = 0; return 0.0;
|
*ok = 0;
|
||||||
|
return 0.0;
|
||||||
}
|
}
|
||||||
p = epos;
|
p = epos;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!saw_digit || (!saw_dot && !saw_exp)) {
|
if (!saw_digit || (!saw_dot && !saw_exp)) {
|
||||||
*ok = 0; return 0.0;
|
*ok = 0;
|
||||||
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create temporary buffer to parse with strtod safely */
|
/* Create temporary buffer to parse with strtod safely */
|
||||||
size_t n = p - start;
|
size_t n = p - start;
|
||||||
char *tmp = (char*)malloc(n + 1);
|
char *tmp = (char *)malloc(n + 1);
|
||||||
if (!tmp) { *ok = 0; return 0.0; }
|
if (!tmp) {
|
||||||
|
*ok = 0;
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
memcpy(tmp, src + start, n);
|
memcpy(tmp, src + start, n);
|
||||||
tmp[n] = '\0';
|
tmp[n] = '\0';
|
||||||
|
|
||||||
char *endp = NULL;
|
char *endp = NULL;
|
||||||
double dv = strtod(tmp, &endp);
|
double dv = strtod(tmp, &endp);
|
||||||
if (!endp || *endp != '\0') { free(tmp); *ok = 0; return 0.0; }
|
if (!endp || *endp != '\0') {
|
||||||
|
free(tmp);
|
||||||
|
*ok = 0;
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
*pos = p;
|
*pos = p;
|
||||||
*ok = 1;
|
*ok = 1;
|
||||||
|
|
|
||||||
732
src/repl.c
732
src/repl.c
File diff suppressed because it is too large
Load diff
|
|
@ -20,7 +20,7 @@ char *string_substr(const char *s, int start, int len) {
|
||||||
if (start > n) start = n;
|
if (start > n) start = n;
|
||||||
if (len < 0) len = 0;
|
if (len < 0) len = 0;
|
||||||
if (start + len > n) len = n - start;
|
if (start + len > n) len = n - start;
|
||||||
char *out = (char*)malloc((size_t)len + 1);
|
char *out = (char *)malloc((size_t)len + 1);
|
||||||
if (!out) return strdup("");
|
if (!out) return strdup("");
|
||||||
memcpy(out, s + start, (size_t)len);
|
memcpy(out, s + start, (size_t)len);
|
||||||
out[len] = '\0';
|
out[len] = '\0';
|
||||||
|
|
@ -42,14 +42,15 @@ Value string_split_to_array(const char *s, const char *sep) {
|
||||||
/* split into characters */
|
/* split into characters */
|
||||||
int n = (int)strlen(s);
|
int n = (int)strlen(s);
|
||||||
if (n <= 0) return make_array_from_values(NULL, 0);
|
if (n <= 0) return make_array_from_values(NULL, 0);
|
||||||
Value *tmp = (Value*)malloc(sizeof(Value) * n);
|
Value *tmp = (Value *)malloc(sizeof(Value) * n);
|
||||||
if (!tmp) return make_array_from_values(NULL, 0);
|
if (!tmp) return make_array_from_values(NULL, 0);
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
char ch[2] = { s[i], 0 };
|
char ch[2] = {s[i], 0};
|
||||||
tmp[i] = make_string(ch);
|
tmp[i] = make_string(ch);
|
||||||
}
|
}
|
||||||
Value arr = make_array_from_values(tmp, n);
|
Value arr = make_array_from_values(tmp, n);
|
||||||
for (int i = 0; i < n; ++i) free_value(tmp[i]);
|
for (int i = 0; i < n; ++i)
|
||||||
|
free_value(tmp[i]);
|
||||||
free(tmp);
|
free(tmp);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
@ -61,13 +62,13 @@ Value string_split_to_array(const char *s, const char *sep) {
|
||||||
const char *pos = NULL;
|
const char *pos = NULL;
|
||||||
while ((pos = strstr(cur, sep)) != NULL) {
|
while ((pos = strstr(cur, sep)) != NULL) {
|
||||||
int len = (int)(pos - cur);
|
int len = (int)(pos - cur);
|
||||||
char *piece = (char*)malloc((size_t)len + 1);
|
char *piece = (char *)malloc((size_t)len + 1);
|
||||||
if (!piece) break;
|
if (!piece) break;
|
||||||
memcpy(piece, cur, (size_t)len);
|
memcpy(piece, cur, (size_t)len);
|
||||||
piece[len] = '\0';
|
piece[len] = '\0';
|
||||||
if (count >= cap) {
|
if (count >= cap) {
|
||||||
cap = cap == 0 ? 4 : cap * 2;
|
cap = cap == 0 ? 4 : cap * 2;
|
||||||
parts = (Value*)realloc(parts, sizeof(Value) * cap);
|
parts = (Value *)realloc(parts, sizeof(Value) * cap);
|
||||||
}
|
}
|
||||||
parts[count++] = make_string(piece);
|
parts[count++] = make_string(piece);
|
||||||
free(piece);
|
free(piece);
|
||||||
|
|
@ -77,13 +78,14 @@ Value string_split_to_array(const char *s, const char *sep) {
|
||||||
char *tail = strdup(cur ? cur : "");
|
char *tail = strdup(cur ? cur : "");
|
||||||
if (count >= cap) {
|
if (count >= cap) {
|
||||||
cap = cap == 0 ? 1 : cap + 1;
|
cap = cap == 0 ? 1 : cap + 1;
|
||||||
parts = (Value*)realloc(parts, sizeof(Value) * cap);
|
parts = (Value *)realloc(parts, sizeof(Value) * cap);
|
||||||
}
|
}
|
||||||
parts[count++] = make_string(tail ? tail : "");
|
parts[count++] = make_string(tail ? tail : "");
|
||||||
free(tail);
|
free(tail);
|
||||||
|
|
||||||
Value arr = make_array_from_values(parts, count);
|
Value arr = make_array_from_values(parts, count);
|
||||||
for (int i = 0; i < count; ++i) free_value(parts[i]);
|
for (int i = 0; i < count; ++i)
|
||||||
|
free_value(parts[i]);
|
||||||
free(parts);
|
free(parts);
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +97,7 @@ char *array_join_with_sep(const Value *v, const char *sep) {
|
||||||
const int n = array_length(v);
|
const int n = array_length(v);
|
||||||
if (n <= 0) return strdup("");
|
if (n <= 0) return strdup("");
|
||||||
|
|
||||||
char **parts = (char**)malloc(sizeof(char*) * n);
|
char **parts = (char **)malloc(sizeof(char *) * n);
|
||||||
if (!parts) return strdup("");
|
if (!parts) return strdup("");
|
||||||
|
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
|
|
@ -111,9 +113,10 @@ char *array_join_with_sep(const Value *v, const char *sep) {
|
||||||
if (i + 1 < n) total += strlen(sep);
|
if (i + 1 < n) total += strlen(sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *out = (char*)malloc(total + 1);
|
char *out = (char *)malloc(total + 1);
|
||||||
if (!out) {
|
if (!out) {
|
||||||
for (int i = 0; i < n; ++i) free(parts[i]);
|
for (int i = 0; i < n; ++i)
|
||||||
|
free(parts[i]);
|
||||||
free(parts);
|
free(parts);
|
||||||
return strdup("");
|
return strdup("");
|
||||||
}
|
}
|
||||||
|
|
@ -121,10 +124,12 @@ char *array_join_with_sep(const Value *v, const char *sep) {
|
||||||
size_t off = 0;
|
size_t off = 0;
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
size_t li = strlen(parts[i]);
|
size_t li = strlen(parts[i]);
|
||||||
memcpy(out + off, parts[i], li); off += li;
|
memcpy(out + off, parts[i], li);
|
||||||
|
off += li;
|
||||||
if (i + 1 < n) {
|
if (i + 1 < n) {
|
||||||
size_t ls = strlen(sep);
|
size_t ls = strlen(sep);
|
||||||
memcpy(out + off, sep, ls); off += ls;
|
memcpy(out + off, sep, ls);
|
||||||
|
off += ls;
|
||||||
}
|
}
|
||||||
free(parts[i]);
|
free(parts[i]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@
|
||||||
* https://opensource.org/license/apache-2-0
|
* https://opensource.org/license/apache-2-0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "vm.h"
|
|
||||||
#include "bytecode.h"
|
#include "bytecode.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include "vm.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
@ -49,7 +49,7 @@ int main() {
|
||||||
|
|
||||||
/* --- Rust FFI demo: call a Rust opcode and string function --- */
|
/* --- Rust FFI demo: call a Rust opcode and string function --- */
|
||||||
#ifdef FUN_WITH_RUST
|
#ifdef FUN_WITH_RUST
|
||||||
extern int fun_op_radd(VM *vm);
|
extern int fun_op_radd(VM * vm);
|
||||||
extern const char *fun_rust_get_string(void);
|
extern const char *fun_rust_get_string(void);
|
||||||
|
|
||||||
printf("=== Rust FFI demo ===\n");
|
printf("=== Rust FFI demo ===\n");
|
||||||
|
|
|
||||||
115
src/value.c
115
src/value.c
|
|
@ -8,13 +8,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* Compile helper implementations into this TU to avoid build system changes */
|
/* Compile helper implementations into this TU to avoid build system changes */
|
||||||
#include "str_utils.c"
|
|
||||||
#include "array_utils.c"
|
#include "array_utils.c"
|
||||||
|
#include "str_utils.c"
|
||||||
|
|
||||||
typedef struct Array {
|
typedef struct Array {
|
||||||
int refcount;
|
int refcount;
|
||||||
|
|
@ -54,8 +54,10 @@ Value make_bool(int v) {
|
||||||
Value make_string(const char *s) {
|
Value make_string(const char *s) {
|
||||||
Value val;
|
Value val;
|
||||||
val.type = VAL_STRING;
|
val.type = VAL_STRING;
|
||||||
if (s) val.s = strdup(s);
|
if (s)
|
||||||
else val.s = strdup("");
|
val.s = strdup(s);
|
||||||
|
else
|
||||||
|
val.s = strdup("");
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,10 +74,9 @@ Value make_nil(void) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value make_array_from_values(const Value *vals, int count) {
|
Value make_array_from_values(const Value *vals, int count) {
|
||||||
if (count < 0) count = 0;
|
if (count < 0) count = 0;
|
||||||
Array *arr = (Array*)malloc(sizeof(Array));
|
Array *arr = (Array *)malloc(sizeof(Array));
|
||||||
if (!arr) {
|
if (!arr) {
|
||||||
Value nil = make_nil();
|
Value nil = make_nil();
|
||||||
return nil;
|
return nil;
|
||||||
|
|
@ -83,7 +84,7 @@ Value make_array_from_values(const Value *vals, int count) {
|
||||||
arr->refcount = 1;
|
arr->refcount = 1;
|
||||||
arr->count = count;
|
arr->count = count;
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
arr->items = (Value*)malloc(sizeof(Value) * count);
|
arr->items = (Value *)malloc(sizeof(Value) * count);
|
||||||
if (!arr->items) {
|
if (!arr->items) {
|
||||||
free(arr);
|
free(arr);
|
||||||
Value nil = make_nil();
|
Value nil = make_nil();
|
||||||
|
|
@ -97,19 +98,19 @@ Value make_array_from_values(const Value *vals, int count) {
|
||||||
}
|
}
|
||||||
Value v;
|
Value v;
|
||||||
v.type = VAL_ARRAY;
|
v.type = VAL_ARRAY;
|
||||||
v.arr = (struct Array*)arr;
|
v.arr = (struct Array *)arr;
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
int array_length(const Value *v) {
|
int array_length(const Value *v) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||||
const Array *a = (const Array*)v->arr;
|
const Array *a = (const Array *)v->arr;
|
||||||
return a->count;
|
return a->count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int array_get_copy(const Value *v, int index, Value *out) {
|
int array_get_copy(const Value *v, int index, Value *out) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||||
const Array *a = (const Array*)v->arr;
|
const Array *a = (const Array *)v->arr;
|
||||||
if (index < 0 || index >= a->count) return 0;
|
if (index < 0 || index >= a->count) return 0;
|
||||||
if (out) *out = copy_value(&a->items[index]);
|
if (out) *out = copy_value(&a->items[index]);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -117,7 +118,7 @@ int array_get_copy(const Value *v, int index, Value *out) {
|
||||||
|
|
||||||
int array_set(Value *v, int index, Value newElem) {
|
int array_set(Value *v, int index, Value newElem) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||||
Array *a = (Array*)v->arr;
|
Array *a = (Array *)v->arr;
|
||||||
if (index < 0 || index >= a->count) return 0;
|
if (index < 0 || index >= a->count) return 0;
|
||||||
free_value(a->items[index]);
|
free_value(a->items[index]);
|
||||||
a->items[index] = newElem; /* take ownership */
|
a->items[index] = newElem; /* take ownership */
|
||||||
|
|
@ -130,8 +131,9 @@ static int ensure_array_capacity(Array *a, int newCount) {
|
||||||
int curr = a->count;
|
int curr = a->count;
|
||||||
int cap = curr;
|
int cap = curr;
|
||||||
if (cap < 4) cap = 4;
|
if (cap < 4) cap = 4;
|
||||||
while (cap < newCount) cap *= 2;
|
while (cap < newCount)
|
||||||
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * cap);
|
cap *= 2;
|
||||||
|
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * cap);
|
||||||
if (!newItems) return 0;
|
if (!newItems) return 0;
|
||||||
/* if growing beyond current count, initialize new slots to nil */
|
/* if growing beyond current count, initialize new slots to nil */
|
||||||
if (cap > a->count) {
|
if (cap > a->count) {
|
||||||
|
|
@ -145,10 +147,13 @@ static int ensure_array_capacity(Array *a, int newCount) {
|
||||||
|
|
||||||
int array_push(Value *v, Value newElem) {
|
int array_push(Value *v, Value newElem) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||||
Array *a = (Array*)v->arr;
|
Array *a = (Array *)v->arr;
|
||||||
/* ensure capacity for count+1 by reallocating items array to at least count+1 elements */
|
/* ensure capacity for count+1 by reallocating items array to at least count+1 elements */
|
||||||
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1));
|
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
|
||||||
if (!newItems) { free_value(newElem); return -1; }
|
if (!newItems) {
|
||||||
|
free_value(newElem);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
a->items = newItems;
|
a->items = newItems;
|
||||||
a->items[a->count] = newElem; /* take ownership */
|
a->items[a->count] = newElem; /* take ownership */
|
||||||
a->count += 1;
|
a->count += 1;
|
||||||
|
|
@ -157,22 +162,27 @@ int array_push(Value *v, Value newElem) {
|
||||||
|
|
||||||
int array_pop(Value *v, Value *out) {
|
int array_pop(Value *v, Value *out) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||||
Array *a = (Array*)v->arr;
|
Array *a = (Array *)v->arr;
|
||||||
if (a->count <= 0) return 0;
|
if (a->count <= 0) return 0;
|
||||||
int idx = a->count - 1;
|
int idx = a->count - 1;
|
||||||
if (out) *out = a->items[idx]; /* transfer ownership */
|
if (out)
|
||||||
else free_value(a->items[idx]);
|
*out = a->items[idx]; /* transfer ownership */
|
||||||
|
else
|
||||||
|
free_value(a->items[idx]);
|
||||||
a->count -= 1;
|
a->count -= 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int array_insert(Value *v, int index, Value newElem) {
|
int array_insert(Value *v, int index, Value newElem) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
if (!v || v->type != VAL_ARRAY || !v->arr) return -1;
|
||||||
Array *a = (Array*)v->arr;
|
Array *a = (Array *)v->arr;
|
||||||
if (index < 0) index = 0;
|
if (index < 0) index = 0;
|
||||||
if (index > a->count) index = a->count;
|
if (index > a->count) index = a->count;
|
||||||
Value *newItems = (Value*)realloc(a->items, sizeof(Value) * (a->count + 1));
|
Value *newItems = (Value *)realloc(a->items, sizeof(Value) * (a->count + 1));
|
||||||
if (!newItems) { free_value(newElem); return -1; }
|
if (!newItems) {
|
||||||
|
free_value(newElem);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
a->items = newItems;
|
a->items = newItems;
|
||||||
/* shift right */
|
/* shift right */
|
||||||
for (int i = a->count; i > index; --i) {
|
for (int i = a->count; i > index; --i) {
|
||||||
|
|
@ -185,10 +195,12 @@ int array_insert(Value *v, int index, Value newElem) {
|
||||||
|
|
||||||
int array_remove(Value *v, int index, Value *out) {
|
int array_remove(Value *v, int index, Value *out) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
if (!v || v->type != VAL_ARRAY || !v->arr) return 0;
|
||||||
Array *a = (Array*)v->arr;
|
Array *a = (Array *)v->arr;
|
||||||
if (index < 0 || index >= a->count) return 0;
|
if (index < 0 || index >= a->count) return 0;
|
||||||
if (out) *out = a->items[index]; /* transfer ownership */
|
if (out)
|
||||||
else free_value(a->items[index]);
|
*out = a->items[index]; /* transfer ownership */
|
||||||
|
else
|
||||||
|
free_value(a->items[index]);
|
||||||
/* shift left */
|
/* shift left */
|
||||||
for (int i = index; i < a->count - 1; ++i) {
|
for (int i = index; i < a->count - 1; ++i) {
|
||||||
a->items[i] = a->items[i + 1];
|
a->items[i] = a->items[i + 1];
|
||||||
|
|
@ -199,7 +211,7 @@ int array_remove(Value *v, int index, Value *out) {
|
||||||
|
|
||||||
Value array_slice(const Value *v, int start, int end) {
|
Value array_slice(const Value *v, int start, int end) {
|
||||||
if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil();
|
if (!v || v->type != VAL_ARRAY || !v->arr) return make_nil();
|
||||||
const Array *a = (const Array*)v->arr;
|
const Array *a = (const Array *)v->arr;
|
||||||
int n = a->count;
|
int n = a->count;
|
||||||
if (start < 0) start = 0;
|
if (start < 0) start = 0;
|
||||||
if (end < 0 || end > n) end = n;
|
if (end < 0 || end > n) end = n;
|
||||||
|
|
@ -213,16 +225,18 @@ Value array_slice(const Value *v, int start, int end) {
|
||||||
|
|
||||||
Value array_concat(const Value *av, const Value *bv) {
|
Value array_concat(const Value *av, const Value *bv) {
|
||||||
if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil();
|
if (!av || !bv || av->type != VAL_ARRAY || bv->type != VAL_ARRAY) return make_nil();
|
||||||
const Array *a = (const Array*)av->arr;
|
const Array *a = (const Array *)av->arr;
|
||||||
const Array *b = (const Array*)bv->arr;
|
const Array *b = (const Array *)bv->arr;
|
||||||
int na = a ? a->count : 0;
|
int na = a ? a->count : 0;
|
||||||
int nb = b ? b->count : 0;
|
int nb = b ? b->count : 0;
|
||||||
int total = na + nb;
|
int total = na + nb;
|
||||||
if (total <= 0) return make_array_from_values(NULL, 0);
|
if (total <= 0) return make_array_from_values(NULL, 0);
|
||||||
Value *tmp = (Value*)malloc(sizeof(Value) * total);
|
Value *tmp = (Value *)malloc(sizeof(Value) * total);
|
||||||
if (!tmp) return make_nil();
|
if (!tmp) return make_nil();
|
||||||
for (int i = 0; i < na; ++i) tmp[i] = a->items[i];
|
for (int i = 0; i < na; ++i)
|
||||||
for (int j = 0; j < nb; ++j) tmp[na + j] = b->items[j];
|
tmp[i] = a->items[i];
|
||||||
|
for (int j = 0; j < nb; ++j)
|
||||||
|
tmp[na + j] = b->items[j];
|
||||||
Value out = make_array_from_values(tmp, total);
|
Value out = make_array_from_values(tmp, total);
|
||||||
/* free temporaries we copied from (deep copy in make_array_from_values) */
|
/* free temporaries we copied from (deep copy in make_array_from_values) */
|
||||||
free(tmp);
|
free(tmp);
|
||||||
|
|
@ -249,14 +263,14 @@ Value copy_value(const Value *v) {
|
||||||
out.fn = v->fn; /* shallow copy pointer */
|
out.fn = v->fn; /* shallow copy pointer */
|
||||||
break;
|
break;
|
||||||
case VAL_ARRAY: {
|
case VAL_ARRAY: {
|
||||||
Array *a = (Array*)v->arr;
|
Array *a = (Array *)v->arr;
|
||||||
out.arr = (struct Array*)a;
|
out.arr = (struct Array *)a;
|
||||||
if (a) a->refcount++;
|
if (a) a->refcount++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VAL_MAP: {
|
case VAL_MAP: {
|
||||||
Map *m = (Map*)v->map;
|
Map *m = (Map *)v->map;
|
||||||
out.map = (struct Map*)m;
|
out.map = (struct Map *)m;
|
||||||
if (m) m->refcount++;
|
if (m) m->refcount++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -281,12 +295,12 @@ Value deep_copy_value(const Value *v) {
|
||||||
case VAL_FUNCTION:
|
case VAL_FUNCTION:
|
||||||
return make_function(v->fn); /* shallow pointer for function bytecode */
|
return make_function(v->fn); /* shallow pointer for function bytecode */
|
||||||
case VAL_ARRAY: {
|
case VAL_ARRAY: {
|
||||||
const Array *a = (const Array*)v->arr;
|
const Array *a = (const Array *)v->arr;
|
||||||
if (!a || a->count <= 0) {
|
if (!a || a->count <= 0) {
|
||||||
return make_array_from_values(NULL, 0);
|
return make_array_from_values(NULL, 0);
|
||||||
}
|
}
|
||||||
/* copy items deeply */
|
/* copy items deeply */
|
||||||
Value *tmp = (Value*)malloc(sizeof(Value) * a->count);
|
Value *tmp = (Value *)malloc(sizeof(Value) * a->count);
|
||||||
if (!tmp) return make_nil();
|
if (!tmp) return make_nil();
|
||||||
for (int i = 0; i < a->count; ++i) {
|
for (int i = 0; i < a->count; ++i) {
|
||||||
tmp[i] = deep_copy_value(&a->items[i]);
|
tmp[i] = deep_copy_value(&a->items[i]);
|
||||||
|
|
@ -299,7 +313,7 @@ Value deep_copy_value(const Value *v) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
case VAL_MAP: {
|
case VAL_MAP: {
|
||||||
const Map *m = (const Map*)v->map;
|
const Map *m = (const Map *)v->map;
|
||||||
if (!m || m->count <= 0) return make_map_empty();
|
if (!m || m->count <= 0) return make_map_empty();
|
||||||
Value out = make_map_empty();
|
Value out = make_map_empty();
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
|
|
@ -318,7 +332,7 @@ void free_value(Value v) {
|
||||||
if (v.type == VAL_STRING && v.s) {
|
if (v.type == VAL_STRING && v.s) {
|
||||||
free(v.s);
|
free(v.s);
|
||||||
} else if (v.type == VAL_ARRAY && v.arr) {
|
} else if (v.type == VAL_ARRAY && v.arr) {
|
||||||
Array *a = (Array*)v.arr;
|
Array *a = (Array *)v.arr;
|
||||||
if (--a->refcount == 0) {
|
if (--a->refcount == 0) {
|
||||||
for (int i = 0; i < a->count; ++i) {
|
for (int i = 0; i < a->count; ++i) {
|
||||||
free_value(a->items[i]);
|
free_value(a->items[i]);
|
||||||
|
|
@ -327,7 +341,7 @@ void free_value(Value v) {
|
||||||
free(a);
|
free(a);
|
||||||
}
|
}
|
||||||
} else if (v.type == VAL_MAP && v.map) {
|
} else if (v.type == VAL_MAP && v.map) {
|
||||||
Map *m = (Map*)v.map;
|
Map *m = (Map *)v.map;
|
||||||
if (--m->refcount == 0) {
|
if (--m->refcount == 0) {
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
if (m->keys[i]) free(m->keys[i]);
|
if (m->keys[i]) free(m->keys[i]);
|
||||||
|
|
@ -356,10 +370,10 @@ void print_value(const Value *v) {
|
||||||
printf("%s", v->i ? "true" : "false");
|
printf("%s", v->i ? "true" : "false");
|
||||||
break;
|
break;
|
||||||
case VAL_FUNCTION:
|
case VAL_FUNCTION:
|
||||||
printf("<function@%p>", (void*)v->fn);
|
printf("<function@%p>", (void *)v->fn);
|
||||||
break;
|
break;
|
||||||
case VAL_ARRAY: {
|
case VAL_ARRAY: {
|
||||||
const Array *a = (const Array*)v->arr;
|
const Array *a = (const Array *)v->arr;
|
||||||
printf("[");
|
printf("[");
|
||||||
if (a) {
|
if (a) {
|
||||||
for (int i = 0; i < a->count; ++i) {
|
for (int i = 0; i < a->count; ++i) {
|
||||||
|
|
@ -371,7 +385,7 @@ void print_value(const Value *v) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VAL_MAP: {
|
case VAL_MAP: {
|
||||||
const Map *m = (const Map*)v->map;
|
const Map *m = (const Map *)v->map;
|
||||||
printf("{");
|
printf("{");
|
||||||
if (m) {
|
if (m) {
|
||||||
for (int i = 0; i < m->count; ++i) {
|
for (int i = 0; i < m->count; ++i) {
|
||||||
|
|
@ -403,7 +417,7 @@ int value_is_truthy(const Value *v) {
|
||||||
case VAL_FUNCTION:
|
case VAL_FUNCTION:
|
||||||
return 1;
|
return 1;
|
||||||
case VAL_ARRAY: {
|
case VAL_ARRAY: {
|
||||||
const Array *a = (const Array*)v->arr;
|
const Array *a = (const Array *)v->arr;
|
||||||
return a && a->count > 0;
|
return a && a->count > 0;
|
||||||
}
|
}
|
||||||
case VAL_NIL:
|
case VAL_NIL:
|
||||||
|
|
@ -432,7 +446,7 @@ char *value_to_string_alloc(const Value *v) {
|
||||||
case VAL_BOOL:
|
case VAL_BOOL:
|
||||||
return strdup(v->i ? "true" : "false");
|
return strdup(v->i ? "true" : "false");
|
||||||
case VAL_FUNCTION: {
|
case VAL_FUNCTION: {
|
||||||
snprintf(buf, sizeof(buf), "<function@%p>", (void*)v->fn);
|
snprintf(buf, sizeof(buf), "<function@%p>", (void *)v->fn);
|
||||||
return strdup(buf);
|
return strdup(buf);
|
||||||
}
|
}
|
||||||
case VAL_ARRAY: {
|
case VAL_ARRAY: {
|
||||||
|
|
@ -444,7 +458,7 @@ char *value_to_string_alloc(const Value *v) {
|
||||||
case VAL_MAP: {
|
case VAL_MAP: {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
if (v->type == VAL_MAP && v->map) {
|
if (v->type == VAL_MAP && v->map) {
|
||||||
const Map *m = (const Map*)v->map;
|
const Map *m = (const Map *)v->map;
|
||||||
n = m ? m->count : 0;
|
n = m ? m->count : 0;
|
||||||
}
|
}
|
||||||
snprintf(buf, sizeof(buf), "{map n=%d}", n);
|
snprintf(buf, sizeof(buf), "{map n=%d}", n);
|
||||||
|
|
@ -465,13 +479,16 @@ int value_equals(const Value *a, const Value *b) {
|
||||||
}
|
}
|
||||||
if (a->type != b->type) return 0;
|
if (a->type != b->type) return 0;
|
||||||
switch (a->type) {
|
switch (a->type) {
|
||||||
case VAL_INT: return a->i == b->i;
|
case VAL_INT:
|
||||||
case VAL_BOOL: return (a->i != 0) == (b->i != 0);
|
return a->i == b->i;
|
||||||
|
case VAL_BOOL:
|
||||||
|
return (a->i != 0) == (b->i != 0);
|
||||||
case VAL_STRING: {
|
case VAL_STRING: {
|
||||||
const char *sa = a->s ? a->s : "";
|
const char *sa = a->s ? a->s : "";
|
||||||
const char *sb = b->s ? b->s : "";
|
const char *sb = b->s ? b->s : "";
|
||||||
return strcmp(sa, sb) == 0;
|
return strcmp(sa, sb) == 0;
|
||||||
}
|
}
|
||||||
default: return 0;
|
default:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file value.h
|
* @file value.h
|
||||||
* @brief Defines the Value type and associated functions for the Fun VM.
|
* @brief Defines the Value type and associated functions for the Fun VM.
|
||||||
*
|
*
|
||||||
* This file defines the `Value` type, which represents all possible data types
|
* This file defines the `Value` type, which represents all possible data types
|
||||||
|
|
@ -90,7 +90,7 @@ Value array_concat(const Value *a, const Value *b); /* returns new arra
|
||||||
/* maps (string keys) */
|
/* maps (string keys) */
|
||||||
Value make_map_empty(void); /* new empty map */
|
Value make_map_empty(void); /* new empty map */
|
||||||
int map_set(Value *m, const char *key, Value v); /* 1 on ok (takes ownership of v) */
|
int map_set(Value *m, const char *key, Value v); /* 1 on ok (takes ownership of v) */
|
||||||
int map_get_copy(const Value *m, const char *key, Value *out);/* 1 on found, out=copy */
|
int map_get_copy(const Value *m, const char *key, Value *out); /* 1 on found, out=copy */
|
||||||
int map_has(const Value *m, const char *key); /* 1/0 */
|
int map_has(const Value *m, const char *key); /* 1/0 */
|
||||||
Value map_keys_array(const Value *m); /* array of strings */
|
Value map_keys_array(const Value *m); /* array of strings */
|
||||||
Value map_values_array(const Value *m); /* array of values (copies) */
|
Value map_values_array(const Value *m); /* array of values (copies) */
|
||||||
|
|
|
||||||
601
src/vm.c
601
src/vm.c
|
|
@ -18,25 +18,25 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#ifdef __unix__
|
#ifdef __unix__
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
/* For hidden input (password) handling in OP_INPUT_LINE */
|
/* For hidden input (password) handling in OP_INPUT_LINE */
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
//#include <arpa/inet.h>
|
// #include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
@ -52,8 +52,8 @@
|
||||||
|
|
||||||
/* Shared Notcurses state (optional) */
|
/* Shared Notcurses state (optional) */
|
||||||
#ifdef FUN_WITH_NOTCURSES
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
# include <wchar.h> /* ensure wcwidth/wcswidth prototypes present before notcurses.h on some systems */
|
#include <notcurses/notcurses.h>
|
||||||
# include <notcurses/notcurses.h>
|
#include <wchar.h> /* ensure wcwidth/wcswidth prototypes present before notcurses.h on some systems */
|
||||||
#endif
|
#endif
|
||||||
#include "vm/notcurses/common.h"
|
#include "vm/notcurses/common.h"
|
||||||
|
|
||||||
|
|
@ -67,14 +67,14 @@
|
||||||
/* Note: INI opcode handlers are included below; changes in vm/ini/ .c files
|
/* Note: INI opcode handlers are included below; changes in vm/ini/ .c files
|
||||||
* require vm.c to rebuild. */
|
* require vm.c to rebuild. */
|
||||||
#include "external/json.c"
|
#include "external/json.c"
|
||||||
|
#include "external/libressl.c"
|
||||||
#include "external/libsql.c"
|
#include "external/libsql.c"
|
||||||
#include "external/pcsc.c"
|
#include "external/openssl.c"
|
||||||
#include "external/pcre2.c"
|
#include "external/pcre2.c"
|
||||||
|
#include "external/pcsc.c"
|
||||||
#include "external/sqlite.c"
|
#include "external/sqlite.c"
|
||||||
#include "external/tcltk.c"
|
#include "external/tcltk.c"
|
||||||
#include "external/xml2.c"
|
#include "external/xml2.c"
|
||||||
#include "external/openssl.c"
|
|
||||||
#include "external/libressl.c"
|
|
||||||
|
|
||||||
/* forward declarations for include mapping used in error reporting */
|
/* forward declarations for include mapping used in error reporting */
|
||||||
extern char *preprocess_includes(const char *src);
|
extern char *preprocess_includes(const char *src);
|
||||||
|
|
@ -105,21 +105,25 @@ static int fun_vm_vfprintf(FILE *stream, const char *fmt, va_list ap) {
|
||||||
/* derive opcode name */
|
/* derive opcode name */
|
||||||
if (ip >= 0 && ip < f->fn->instr_count) {
|
if (ip >= 0 && ip < f->fn->instr_count) {
|
||||||
int op = f->fn->instructions[ip].op;
|
int op = f->fn->instructions[ip].op;
|
||||||
if (op >= 0 && op < (int)(sizeof(opcode_names)/sizeof(opcode_names[0]))) {
|
if (op >= 0 && op < (int)(sizeof(opcode_names) / sizeof(opcode_names[0]))) {
|
||||||
opname = opcode_names[op];
|
opname = opcode_names[op];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* derive source line by scanning back to the most recent OP_LINE marker */
|
/* derive source line by scanning back to the most recent OP_LINE marker */
|
||||||
for (int i = ip; i >= 0; --i) {
|
for (int i = ip; i >= 0; --i) {
|
||||||
Instruction prev = f->fn->instructions[i];
|
Instruction prev = f->fn->instructions[i];
|
||||||
if (prev.op == OP_LINE) { line = prev.operand; break; }
|
if (prev.op == OP_LINE) {
|
||||||
|
line = prev.operand;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* fallback to VM's last recorded line if no marker found */
|
/* fallback to VM's last recorded line if no marker found */
|
||||||
if (line <= 0) line = g_active_vm->current_line > 0 ? g_active_vm->current_line : 1;
|
if (line <= 0) line = g_active_vm->current_line > 0 ? g_active_vm->current_line : 1;
|
||||||
|
|
||||||
/* If this function was compiled from an include-expanded source, map to the included file */
|
/* If this function was compiled from an include-expanded source, map to the included file */
|
||||||
if (sfile && line > 0) {
|
if (sfile && line > 0) {
|
||||||
char mapped_path[1024]; int mapped_line = line;
|
char mapped_path[1024];
|
||||||
|
int mapped_line = line;
|
||||||
if (map_expanded_line_to_include(sfile, line, mapped_path, sizeof(mapped_path), &mapped_line)) {
|
if (map_expanded_line_to_include(sfile, line, mapped_path, sizeof(mapped_path), &mapped_line)) {
|
||||||
sfile = strdup(mapped_path); /* leak on purpose for simplicity; this is rare */
|
sfile = strdup(mapped_path); /* leak on purpose for simplicity; this is rare */
|
||||||
line = mapped_line;
|
line = mapped_line;
|
||||||
|
|
@ -157,10 +161,16 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
|
||||||
if (!f) return 0;
|
if (!f) return 0;
|
||||||
fseek(f, 0, SEEK_END);
|
fseek(f, 0, SEEK_END);
|
||||||
long sz = ftell(f);
|
long sz = ftell(f);
|
||||||
if (sz < 0) { fclose(f); return 0; }
|
if (sz < 0) {
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
rewind(f);
|
rewind(f);
|
||||||
char *buf = (char*)malloc((size_t)sz + 1);
|
char *buf = (char *)malloc((size_t)sz + 1);
|
||||||
if (!buf) { fclose(f); return 0; }
|
if (!buf) {
|
||||||
|
fclose(f);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
size_t n = fread(buf, 1, (size_t)sz, f);
|
size_t n = fread(buf, 1, (size_t)sz, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
buf[n] = '\0';
|
buf[n] = '\0';
|
||||||
|
|
@ -171,12 +181,16 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
|
||||||
|
|
||||||
/* find start offset of requested 1-based line */
|
/* find start offset of requested 1-based line */
|
||||||
size_t len = strlen(prep);
|
size_t len = strlen(prep);
|
||||||
size_t pos = 0; int cur = 1;
|
size_t pos = 0;
|
||||||
|
int cur = 1;
|
||||||
while (pos < len && cur < line) {
|
while (pos < len && cur < line) {
|
||||||
if (prep[pos] == '\n') cur++;
|
if (prep[pos] == '\n') cur++;
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
if (cur != line) { free(prep); return 0; }
|
if (cur != line) {
|
||||||
|
free(prep);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* scan backward to find last include marker line */
|
/* scan backward to find last include marker line */
|
||||||
const char *marker = "// __include_begin__: ";
|
const char *marker = "// __include_begin__: ";
|
||||||
|
|
@ -185,13 +199,15 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
|
||||||
while (scan > 0) {
|
while (scan > 0) {
|
||||||
/* find start of this line */
|
/* find start of this line */
|
||||||
size_t ls = scan;
|
size_t ls = scan;
|
||||||
while (ls > 0 && prep[ls - 1] != '\n') ls--;
|
while (ls > 0 && prep[ls - 1] != '\n')
|
||||||
|
ls--;
|
||||||
/* check marker */
|
/* check marker */
|
||||||
if (ls + mlen <= len && strncmp(prep + ls, marker, mlen) == 0) {
|
if (ls + mlen <= len && strncmp(prep + ls, marker, mlen) == 0) {
|
||||||
/* extract included path up to EOL or ' as ' */
|
/* extract included path up to EOL or ' as ' */
|
||||||
size_t p = ls + mlen;
|
size_t p = ls + mlen;
|
||||||
size_t pe = p;
|
size_t pe = p;
|
||||||
while (pe < len && prep[pe] != '\n' && !(prep[pe] == ' ' && pe + 3 < len && strncmp(prep + pe, " as ", 4) == 0)) pe++;
|
while (pe < len && prep[pe] != '\n' && !(prep[pe] == ' ' && pe + 3 < len && strncmp(prep + pe, " as ", 4) == 0))
|
||||||
|
pe++;
|
||||||
size_t copy = (pe - p) < (out_path_cap - 1) ? (pe - p) : (out_path_cap - 1);
|
size_t copy = (pe - p) < (out_path_cap - 1) ? (pe - p) : (out_path_cap - 1);
|
||||||
memcpy(out_path, prep + p, copy);
|
memcpy(out_path, prep + p, copy);
|
||||||
out_path[copy] = '\0';
|
out_path[copy] = '\0';
|
||||||
|
|
@ -199,9 +215,13 @@ static int map_expanded_line_to_include(const char *path, int line, char *out_pa
|
||||||
int inner = 1;
|
int inner = 1;
|
||||||
size_t q = pe;
|
size_t q = pe;
|
||||||
/* skip to next line start */
|
/* skip to next line start */
|
||||||
while (q < len && prep[q] != '\n') q++;
|
while (q < len && prep[q] != '\n')
|
||||||
|
q++;
|
||||||
if (q < len && prep[q] == '\n') q++;
|
if (q < len && prep[q] == '\n') q++;
|
||||||
while (q < pos) { if (prep[q] == '\n') inner++; q++; }
|
while (q < pos) {
|
||||||
|
if (prep[q] == '\n') inner++;
|
||||||
|
q++;
|
||||||
|
}
|
||||||
*out_line = inner;
|
*out_line = inner;
|
||||||
free(prep);
|
free(prep);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -308,17 +328,26 @@ Dev tips:
|
||||||
- You can run scripts/run_examples.sh to sanity-check examples quickly.
|
- You can run scripts/run_examples.sh to sanity-check examples quickly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const char* value_type_name(ValueType t) {
|
static const char *value_type_name(ValueType t) {
|
||||||
switch (t) {
|
switch (t) {
|
||||||
case VAL_FUNCTION: return "function";
|
case VAL_FUNCTION:
|
||||||
case VAL_INT: return "int";
|
return "function";
|
||||||
case VAL_FLOAT: return "float";
|
case VAL_INT:
|
||||||
case VAL_BOOL: return "boolean";
|
return "int";
|
||||||
case VAL_ARRAY: return "array";
|
case VAL_FLOAT:
|
||||||
case VAL_MAP: return "map";
|
return "float";
|
||||||
case VAL_NIL: return "nil";
|
case VAL_BOOL:
|
||||||
case VAL_STRING: return "string";
|
return "boolean";
|
||||||
default: return "unknown";
|
case VAL_ARRAY:
|
||||||
|
return "array";
|
||||||
|
case VAL_MAP:
|
||||||
|
return "map";
|
||||||
|
case VAL_NIL:
|
||||||
|
return "nil";
|
||||||
|
case VAL_STRING:
|
||||||
|
return "string";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,7 +357,8 @@ void vm_clear_output(VM *vm) {
|
||||||
}
|
}
|
||||||
vm->output_count = 0;
|
vm->output_count = 0;
|
||||||
// reset partial flags
|
// reset partial flags
|
||||||
for (int i = 0; i < OUTPUT_SIZE; ++i) vm->output_is_partial[i] = 0;
|
for (int i = 0; i < OUTPUT_SIZE; ++i)
|
||||||
|
vm->output_is_partial[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm_free(VM *vm) {
|
void vm_free(VM *vm) {
|
||||||
|
|
@ -391,7 +421,7 @@ void vm_debug_reset(VM *vm) {
|
||||||
|
|
||||||
int vm_debug_add_breakpoint(VM *vm, const char *file, int line) {
|
int vm_debug_add_breakpoint(VM *vm, const char *file, int line) {
|
||||||
if (!file || line <= 0) return -1;
|
if (!file || line <= 0) return -1;
|
||||||
if (vm->break_count >= (int)(sizeof(vm->breakpoints)/sizeof(vm->breakpoints[0]))) return -1;
|
if (vm->break_count >= (int)(sizeof(vm->breakpoints) / sizeof(vm->breakpoints[0]))) return -1;
|
||||||
int id = vm->break_count++;
|
int id = vm->break_count++;
|
||||||
vm->breakpoints[id].file = strdup(file);
|
vm->breakpoints[id].file = strdup(file);
|
||||||
vm->breakpoints[id].line = line;
|
vm->breakpoints[id].line = line;
|
||||||
|
|
@ -476,7 +506,7 @@ int64_t vm_pop_i64(VM *vm) {
|
||||||
if (v.type == VAL_INT) {
|
if (v.type == VAL_INT) {
|
||||||
out = v.i;
|
out = v.i;
|
||||||
} else if (v.type == VAL_FLOAT) {
|
} else if (v.type == VAL_FLOAT) {
|
||||||
out = (int64_t) v.d;
|
out = (int64_t)v.d;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Runtime type error: expected int/float on stack, got %s\n", value_type_name(v.type));
|
fprintf(stderr, "Runtime type error: expected int/float on stack, got %s\n", value_type_name(v.type));
|
||||||
free_value(v);
|
free_value(v);
|
||||||
|
|
@ -501,7 +531,7 @@ size_t vm_value_sizeof(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *vm_as_mut_ptr(VM *vm) {
|
void *vm_as_mut_ptr(VM *vm) {
|
||||||
return (void*)vm;
|
return (void *)vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t vm_offset_of_exit_code(void) {
|
size_t vm_offset_of_exit_code(void) {
|
||||||
|
|
@ -523,7 +553,8 @@ size_t vm_offset_of_globals(void) {
|
||||||
static void frame_init(Frame *f) {
|
static void frame_init(Frame *f) {
|
||||||
f->fn = NULL;
|
f->fn = NULL;
|
||||||
f->ip = 0;
|
f->ip = 0;
|
||||||
for (int i = 0; i < MAX_FRAME_LOCALS; ++i) f->locals[i] = make_nil();
|
for (int i = 0; i < MAX_FRAME_LOCALS; ++i)
|
||||||
|
f->locals[i] = make_nil();
|
||||||
f->try_sp = -1;
|
f->try_sp = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -531,7 +562,8 @@ void vm_init(VM *vm) {
|
||||||
vm->sp = -1;
|
vm->sp = -1;
|
||||||
vm->fp = -1;
|
vm->fp = -1;
|
||||||
vm->output_count = 0;
|
vm->output_count = 0;
|
||||||
for (int i = 0; i < OUTPUT_SIZE; ++i) vm->output_is_partial[i] = 0;
|
for (int i = 0; i < OUTPUT_SIZE; ++i)
|
||||||
|
vm->output_is_partial[i] = 0;
|
||||||
vm->instr_count = 0;
|
vm->instr_count = 0;
|
||||||
vm->exit_code = 0;
|
vm->exit_code = 0;
|
||||||
vm->trace_enabled = 0;
|
vm->trace_enabled = 0;
|
||||||
|
|
@ -544,7 +576,7 @@ void vm_init(VM *vm) {
|
||||||
vm->debug_step_start_ic = 0;
|
vm->debug_step_start_ic = 0;
|
||||||
vm->debug_stop_requested = 0;
|
vm->debug_stop_requested = 0;
|
||||||
vm->break_count = 0;
|
vm->break_count = 0;
|
||||||
for (int i = 0; i < (int)(sizeof(vm->breakpoints)/sizeof(vm->breakpoints[0])); ++i) {
|
for (int i = 0; i < (int)(sizeof(vm->breakpoints) / sizeof(vm->breakpoints[0])); ++i) {
|
||||||
vm->breakpoints[i].file = NULL;
|
vm->breakpoints[i].file = NULL;
|
||||||
vm->breakpoints[i].line = 0;
|
vm->breakpoints[i].line = 0;
|
||||||
vm->breakpoints[i].active = 0;
|
vm->breakpoints[i].active = 0;
|
||||||
|
|
@ -656,13 +688,15 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
vm->instr_count++; /* count each executed instruction */
|
vm->instr_count++; /* count each executed instruction */
|
||||||
|
|
||||||
if (vm->trace_enabled) {
|
if (vm->trace_enabled) {
|
||||||
const char *opname = (inst.op >= 0 && inst.op < (int)(sizeof(opcode_names)/sizeof(opcode_names[0])))
|
const char *opname = (inst.op >= 0 && inst.op < (int)(sizeof(opcode_names) / sizeof(opcode_names[0])))
|
||||||
? opcode_names[inst.op] : "???";
|
? opcode_names[inst.op]
|
||||||
|
: "???";
|
||||||
const char *fname = f->fn && f->fn->name ? f->fn->name : "<entry>";
|
const char *fname = f->fn && f->fn->name ? f->fn->name : "<entry>";
|
||||||
const char *sfile = f->fn && f->fn->source_file ? f->fn->source_file : "<unknown>";
|
const char *sfile = f->fn && f->fn->source_file ? f->fn->source_file : "<unknown>";
|
||||||
/* Dump up to top 4 stack values */
|
/* Dump up to top 4 stack values */
|
||||||
int count = vm->sp + 1;
|
int count = vm->sp + 1;
|
||||||
int start = count - 4; if (start < 0) start = 0;
|
int start = count - 4;
|
||||||
|
if (start < 0) start = 0;
|
||||||
fprintf(stdout, "TRACE %s:%d %s ip=%d %-14s %d | stack[%d]=[", sfile, vm->current_line, fname, f->ip - 1, opname, inst.operand, count);
|
fprintf(stdout, "TRACE %s:%d %s ip=%d %-14s %d | stack[%d]=[", sfile, vm->current_line, fname, f->ip - 1, opname, inst.operand, count);
|
||||||
for (int i = start; i < count; ++i) {
|
for (int i = start; i < count; ++i) {
|
||||||
char *sv = value_to_string_alloc(&vm->stack[i]);
|
char *sv = value_to_string_alloc(&vm->stack[i]);
|
||||||
|
|
@ -692,231 +726,231 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (inst.op) {
|
switch (inst.op) {
|
||||||
/* All opcode handlers as .c includes */
|
/* All opcode handlers as .c includes */
|
||||||
#include "vm/arithmetic/add.c"
|
#include "vm/arithmetic/add.c"
|
||||||
#include "vm/arithmetic/div.c"
|
#include "vm/arithmetic/div.c"
|
||||||
#include "vm/arithmetic/mul.c"
|
#include "vm/arithmetic/mul.c"
|
||||||
#include "vm/arithmetic/sub.c"
|
#include "vm/arithmetic/sub.c"
|
||||||
|
|
||||||
#include "vm/arrays/apop.c"
|
#include "vm/arrays/apop.c"
|
||||||
#include "vm/arrays/clear.c"
|
#include "vm/arrays/clear.c"
|
||||||
#include "vm/arrays/contains.c"
|
#include "vm/arrays/contains.c"
|
||||||
#include "vm/arrays/enumerate.c"
|
#include "vm/arrays/enumerate.c"
|
||||||
#include "vm/arrays/index_get.c"
|
#include "vm/arrays/index_get.c"
|
||||||
#include "vm/arrays/index_of.c"
|
#include "vm/arrays/index_of.c"
|
||||||
#include "vm/arrays/index_set.c"
|
#include "vm/arrays/index_set.c"
|
||||||
#include "vm/arrays/insert.c"
|
#include "vm/arrays/insert.c"
|
||||||
#include "vm/arrays/join.c"
|
#include "vm/arrays/join.c"
|
||||||
#include "vm/arrays/make_array.c"
|
#include "vm/arrays/make_array.c"
|
||||||
#include "vm/arrays/push.c"
|
#include "vm/arrays/push.c"
|
||||||
#include "vm/arrays/remove.c"
|
#include "vm/arrays/remove.c"
|
||||||
#include "vm/arrays/set.c"
|
#include "vm/arrays/set.c"
|
||||||
#include "vm/arrays/slice.c"
|
#include "vm/arrays/slice.c"
|
||||||
#include "vm/arrays/zip.c"
|
#include "vm/arrays/zip.c"
|
||||||
|
|
||||||
/* Bitwise and shifts/rotates */
|
/* Bitwise and shifts/rotates */
|
||||||
#include "vm/bitwise/band.c"
|
#include "vm/bitwise/band.c"
|
||||||
#include "vm/bitwise/bor.c"
|
#include "vm/bitwise/bnot.c"
|
||||||
#include "vm/bitwise/bxor.c"
|
#include "vm/bitwise/bor.c"
|
||||||
#include "vm/bitwise/bnot.c"
|
#include "vm/bitwise/bxor.c"
|
||||||
#include "vm/bitwise/shl.c"
|
#include "vm/bitwise/rol.c"
|
||||||
#include "vm/bitwise/shr.c"
|
#include "vm/bitwise/ror.c"
|
||||||
#include "vm/bitwise/rol.c"
|
#include "vm/bitwise/shl.c"
|
||||||
#include "vm/bitwise/ror.c"
|
#include "vm/bitwise/shr.c"
|
||||||
|
|
||||||
#include "vm/core/call.c"
|
#include "vm/core/call.c"
|
||||||
#include "vm/core/dup.c"
|
#include "vm/core/dup.c"
|
||||||
#include "vm/core/exit.c"
|
#include "vm/core/exit.c"
|
||||||
#include "vm/core/halt.c"
|
#include "vm/core/halt.c"
|
||||||
#include "vm/core/jump.c"
|
#include "vm/core/jump.c"
|
||||||
#include "vm/core/jump_if_false.c"
|
#include "vm/core/jump_if_false.c"
|
||||||
#include "vm/core/load_const.c"
|
#include "vm/core/load_const.c"
|
||||||
#include "vm/core/load_global.c"
|
#include "vm/core/load_global.c"
|
||||||
#include "vm/core/load_local.c"
|
#include "vm/core/load_local.c"
|
||||||
#include "vm/core/nop.c"
|
#include "vm/core/nop.c"
|
||||||
#include "vm/core/pop.c"
|
#include "vm/core/pop.c"
|
||||||
#include "vm/core/return.c"
|
#include "vm/core/return.c"
|
||||||
#include "vm/core/store_global.c"
|
#include "vm/core/store_global.c"
|
||||||
#include "vm/core/store_local.c"
|
#include "vm/core/store_local.c"
|
||||||
#include "vm/core/swap.c"
|
#include "vm/core/swap.c"
|
||||||
#include "vm/core/throw.c"
|
#include "vm/core/throw.c"
|
||||||
#include "vm/core/try_pop.c"
|
#include "vm/core/try_pop.c"
|
||||||
#include "vm/core/try_push.c"
|
#include "vm/core/try_push.c"
|
||||||
|
|
||||||
#include "vm/io/read_file.c"
|
#include "vm/io/input_line.c"
|
||||||
#include "vm/io/write_file.c"
|
#include "vm/io/read_file.c"
|
||||||
#include "vm/io/input_line.c"
|
#include "vm/io/write_file.c"
|
||||||
|
|
||||||
#include "vm/logic/and.c"
|
#include "vm/logic/and.c"
|
||||||
#include "vm/logic/eq.c"
|
#include "vm/logic/eq.c"
|
||||||
#include "vm/logic/gt.c"
|
#include "vm/logic/gt.c"
|
||||||
#include "vm/logic/gte.c"
|
#include "vm/logic/gte.c"
|
||||||
#include "vm/logic/lt.c"
|
#include "vm/logic/lt.c"
|
||||||
#include "vm/logic/lte.c"
|
#include "vm/logic/lte.c"
|
||||||
#include "vm/logic/neq.c"
|
#include "vm/logic/neq.c"
|
||||||
#include "vm/logic/not.c"
|
#include "vm/logic/not.c"
|
||||||
#include "vm/logic/or.c"
|
#include "vm/logic/or.c"
|
||||||
|
|
||||||
#include "vm/maps/has_key.c"
|
#include "vm/maps/has_key.c"
|
||||||
#include "vm/maps/keys.c"
|
#include "vm/maps/keys.c"
|
||||||
#include "vm/maps/make_map.c"
|
#include "vm/maps/make_map.c"
|
||||||
#include "vm/maps/values.c"
|
#include "vm/maps/values.c"
|
||||||
|
|
||||||
#include "vm/math/abs.c"
|
#include "vm/math/abs.c"
|
||||||
#include "vm/math/clamp.c"
|
#include "vm/math/ceil.c"
|
||||||
#include "vm/math/max.c"
|
#include "vm/math/clamp.c"
|
||||||
#include "vm/math/min.c"
|
#include "vm/math/cos.c"
|
||||||
#include "vm/math/fmax.c"
|
#include "vm/math/exp.c"
|
||||||
#include "vm/math/fmin.c"
|
#include "vm/math/floor.c"
|
||||||
#include "vm/math/mod.c"
|
#include "vm/math/fmax.c"
|
||||||
#include "vm/math/pow.c"
|
#include "vm/math/fmin.c"
|
||||||
#include "vm/math/floor.c"
|
#include "vm/math/gcd.c"
|
||||||
#include "vm/math/ceil.c"
|
#include "vm/math/isqrt.c"
|
||||||
#include "vm/math/trunc.c"
|
#include "vm/math/lcm.c"
|
||||||
#include "vm/math/round.c"
|
#include "vm/math/log.c"
|
||||||
#include "vm/math/sin.c"
|
#include "vm/math/log10.c"
|
||||||
#include "vm/math/cos.c"
|
#include "vm/math/max.c"
|
||||||
#include "vm/math/tan.c"
|
#include "vm/math/min.c"
|
||||||
#include "vm/math/exp.c"
|
#include "vm/math/mod.c"
|
||||||
#include "vm/math/log.c"
|
#include "vm/math/pow.c"
|
||||||
#include "vm/math/log10.c"
|
#include "vm/math/random_int.c"
|
||||||
#include "vm/math/sqrt.c"
|
#include "vm/math/random_seed.c"
|
||||||
#include "vm/math/random_int.c"
|
#include "vm/math/round.c"
|
||||||
#include "vm/math/random_seed.c"
|
#include "vm/math/sign.c"
|
||||||
#include "vm/math/gcd.c"
|
#include "vm/math/sin.c"
|
||||||
#include "vm/math/lcm.c"
|
#include "vm/math/sqrt.c"
|
||||||
#include "vm/math/isqrt.c"
|
#include "vm/math/tan.c"
|
||||||
#include "vm/math/sign.c"
|
#include "vm/math/trunc.c"
|
||||||
|
|
||||||
/* Rust FFI demo opcode(s) */
|
/* Rust FFI demo opcode(s) */
|
||||||
#include "vm/rust/hello.c"
|
#include "vm/rust/get_sp.c"
|
||||||
#include "vm/rust/hello_args.c"
|
#include "vm/rust/hello.c"
|
||||||
#include "vm/rust/hello_args_return.c"
|
#include "vm/rust/hello_args.c"
|
||||||
#include "vm/rust/get_sp.c"
|
#include "vm/rust/hello_args_return.c"
|
||||||
#include "vm/rust/set_exit.c"
|
#include "vm/rust/set_exit.c"
|
||||||
|
|
||||||
#include "vm/os/env.c"
|
#include "vm/os/clock_mono_ms.c"
|
||||||
#include "vm/os/env_all.c"
|
#include "vm/os/date_format.c"
|
||||||
#include "vm/os/fun_version.c"
|
#include "vm/os/env.c"
|
||||||
#include "vm/os/sleep_ms.c"
|
#include "vm/os/env_all.c"
|
||||||
#include "vm/os/thread_join.c"
|
#include "vm/os/fun_version.c"
|
||||||
#include "vm/os/thread_spawn.c"
|
#include "vm/os/proc_run.c"
|
||||||
#include "vm/os/proc_run.c"
|
#include "vm/os/proc_system.c"
|
||||||
#include "vm/os/proc_system.c"
|
#include "vm/os/random_number.c"
|
||||||
#include "vm/os/time_now_ms.c"
|
#include "vm/os/serial_close.c"
|
||||||
#include "vm/os/clock_mono_ms.c"
|
#include "vm/os/serial_config.c"
|
||||||
#include "vm/os/date_format.c"
|
#include "vm/os/serial_open.c"
|
||||||
#include "vm/os/random_number.c"
|
#include "vm/os/serial_recv.c"
|
||||||
#include "vm/os/serial_open.c"
|
#include "vm/os/serial_send.c"
|
||||||
#include "vm/os/serial_config.c"
|
#include "vm/os/sleep_ms.c"
|
||||||
#include "vm/os/serial_send.c"
|
#include "vm/os/thread_join.c"
|
||||||
#include "vm/os/serial_recv.c"
|
#include "vm/os/thread_spawn.c"
|
||||||
#include "vm/os/serial_close.c"
|
#include "vm/os/time_now_ms.c"
|
||||||
|
|
||||||
/* Socket ops */
|
/* Socket ops */
|
||||||
#include "vm/os/socket_tcp_listen.c"
|
#include "vm/os/socket_close.c"
|
||||||
#include "vm/os/socket_tcp_accept.c"
|
#include "vm/os/socket_recv.c"
|
||||||
#include "vm/os/socket_tcp_connect.c"
|
#include "vm/os/socket_send.c"
|
||||||
#include "vm/os/socket_send.c"
|
#include "vm/os/socket_tcp_accept.c"
|
||||||
#include "vm/os/socket_recv.c"
|
#include "vm/os/socket_tcp_connect.c"
|
||||||
#include "vm/os/socket_close.c"
|
#include "vm/os/socket_tcp_listen.c"
|
||||||
#include "vm/os/socket_unix_listen.c"
|
#include "vm/os/socket_unix_connect.c"
|
||||||
#include "vm/os/socket_unix_connect.c"
|
#include "vm/os/socket_unix_listen.c"
|
||||||
|
|
||||||
#ifdef FUN_WITH_PCSC
|
#ifdef FUN_WITH_PCSC
|
||||||
#include "vm/pcsc/establish.c"
|
#include "vm/pcsc/connect.c"
|
||||||
#include "vm/pcsc/release.c"
|
#include "vm/pcsc/disconnect.c"
|
||||||
#include "vm/pcsc/list_readers.c"
|
#include "vm/pcsc/establish.c"
|
||||||
#include "vm/pcsc/connect.c"
|
#include "vm/pcsc/list_readers.c"
|
||||||
#include "vm/pcsc/disconnect.c"
|
#include "vm/pcsc/release.c"
|
||||||
#include "vm/pcsc/transmit.c"
|
#include "vm/pcsc/transmit.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* JSON ops (implemented in jsonc.c, included above) */
|
/* JSON ops (implemented in jsonc.c, included above) */
|
||||||
#ifdef FUN_WITH_JSON
|
#ifdef FUN_WITH_JSON
|
||||||
#include "vm/json/parse.c"
|
#include "vm/json/from_file.c"
|
||||||
#include "vm/json/stringify.c"
|
#include "vm/json/parse.c"
|
||||||
#include "vm/json/from_file.c"
|
#include "vm/json/stringify.c"
|
||||||
#include "vm/json/to_file.c"
|
#include "vm/json/to_file.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* XML ops (libxml2) */
|
/* XML ops (libxml2) */
|
||||||
#ifdef FUN_WITH_XML2
|
#ifdef FUN_WITH_XML2
|
||||||
#include "vm/xml/parse.c"
|
#include "vm/xml/name.c"
|
||||||
#include "vm/xml/root.c"
|
#include "vm/xml/parse.c"
|
||||||
#include "vm/xml/name.c"
|
#include "vm/xml/root.c"
|
||||||
#include "vm/xml/text.c"
|
#include "vm/xml/text.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* INI ops (iniparser 4.2.6) */
|
/* INI ops (iniparser 4.2.6) */
|
||||||
#ifdef FUN_WITH_INI
|
#ifdef FUN_WITH_INI
|
||||||
#include "vm/ini/load.c"
|
#include "vm/ini/free.c"
|
||||||
#include "vm/ini/free.c"
|
#include "vm/ini/get_bool.c"
|
||||||
#include "vm/ini/get_string.c"
|
#include "vm/ini/get_double.c"
|
||||||
#include "vm/ini/get_int.c"
|
#include "vm/ini/get_int.c"
|
||||||
#include "vm/ini/get_double.c"
|
#include "vm/ini/get_string.c"
|
||||||
#include "vm/ini/get_bool.c"
|
#include "vm/ini/load.c"
|
||||||
#include "vm/ini/set.c"
|
#include "vm/ini/save.c"
|
||||||
#include "vm/ini/unset.c"
|
#include "vm/ini/set.c"
|
||||||
#include "vm/ini/save.c"
|
#include "vm/ini/unset.c"
|
||||||
#else
|
#else
|
||||||
#include "vm/ini/stubs.c"
|
#include "vm/ini/stubs.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* CURL ops */
|
/* CURL ops */
|
||||||
#ifdef FUN_WITH_CURL
|
#ifdef FUN_WITH_CURL
|
||||||
#include "vm/curl/get.c"
|
#include "vm/curl/download.c"
|
||||||
#include "vm/curl/post.c"
|
#include "vm/curl/get.c"
|
||||||
#include "vm/curl/download.c"
|
#include "vm/curl/post.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* OpenSSL ops (md5/sha256/sha512/ripemd160) */
|
/* OpenSSL ops (md5/sha256/sha512/ripemd160) */
|
||||||
#ifdef FUN_WITH_OPENSSL
|
#ifdef FUN_WITH_OPENSSL
|
||||||
#include "vm/openssl/md5.c"
|
#include "vm/openssl/md5.c"
|
||||||
#include "vm/openssl/sha256.c"
|
#include "vm/openssl/ripemd160.c"
|
||||||
#include "vm/openssl/sha512.c"
|
#include "vm/openssl/sha256.c"
|
||||||
#include "vm/openssl/ripemd160.c"
|
#include "vm/openssl/sha512.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* LibreSSL ops (md5/sha256/sha512/ripemd160) */
|
/* LibreSSL ops (md5/sha256/sha512/ripemd160) */
|
||||||
#ifdef FUN_WITH_LIBRESSL
|
#ifdef FUN_WITH_LIBRESSL
|
||||||
#include "vm/libressl/md5.c"
|
#include "vm/libressl/md5.c"
|
||||||
#include "vm/libressl/sha256.c"
|
#include "vm/libressl/ripemd160.c"
|
||||||
#include "vm/libressl/sha512.c"
|
#include "vm/libressl/sha256.c"
|
||||||
#include "vm/libressl/ripemd160.c"
|
#include "vm/libressl/sha512.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Tk (Tcl/Tk) ops */
|
/* Tk (Tcl/Tk) ops */
|
||||||
#ifdef FUN_WITH_TCLTK
|
#ifdef FUN_WITH_TCLTK
|
||||||
#include "vm/tk/eval.c"
|
#include "vm/tk/bind.c"
|
||||||
#include "vm/tk/result.c"
|
#include "vm/tk/button.c"
|
||||||
#include "vm/tk/loop.c"
|
#include "vm/tk/eval.c"
|
||||||
#include "vm/tk/wm_title.c"
|
#include "vm/tk/label.c"
|
||||||
#include "vm/tk/label.c"
|
#include "vm/tk/loop.c"
|
||||||
#include "vm/tk/button.c"
|
#include "vm/tk/pack.c"
|
||||||
#include "vm/tk/pack.c"
|
#include "vm/tk/result.c"
|
||||||
#include "vm/tk/bind.c"
|
#include "vm/tk/wm_title.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Notcurses TUI ops (optional) */
|
/* Notcurses TUI ops (optional) */
|
||||||
#ifdef FUN_WITH_NOTCURSES
|
#ifdef FUN_WITH_NOTCURSES
|
||||||
#include "vm/notcurses/init.c"
|
#include "vm/notcurses/clear.c"
|
||||||
#include "vm/notcurses/shutdown.c"
|
#include "vm/notcurses/draw_text.c"
|
||||||
#include "vm/notcurses/clear.c"
|
#include "vm/notcurses/getch.c"
|
||||||
#include "vm/notcurses/draw_text.c"
|
#include "vm/notcurses/init.c"
|
||||||
#include "vm/notcurses/getch.c"
|
#include "vm/notcurses/shutdown.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SQLite ops */
|
/* SQLite ops */
|
||||||
#ifdef FUN_WITH_SQLITE
|
#ifdef FUN_WITH_SQLITE
|
||||||
#include "vm/sqlite/open.c"
|
#include "vm/sqlite/close.c"
|
||||||
#include "vm/sqlite/close.c"
|
#include "vm/sqlite/exec.c"
|
||||||
#include "vm/sqlite/exec.c"
|
#include "vm/sqlite/open.c"
|
||||||
#include "vm/sqlite/query.c"
|
#include "vm/sqlite/query.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* C++ demo opcodes (guarded) */
|
/* C++ demo opcodes (guarded) */
|
||||||
#if defined(FUN_WITH_CPP)
|
#if defined(FUN_WITH_CPP)
|
||||||
case OP_CPP_ADD: {
|
case OP_CPP_ADD: {
|
||||||
int rc = fun_op_cpp_add(vm);
|
int rc = fun_op_cpp_add(vm);
|
||||||
if (rc != 0) {
|
if (rc != 0) {
|
||||||
|
|
@ -924,53 +958,54 @@ void vm_run(VM *vm, Bytecode *entry) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
case OP_CPP_ADD: {
|
case OP_CPP_ADD: {
|
||||||
vm_raise_error(vm, "CPP support is not enabled (build with -DFUN_WITH_CPP=ON)");
|
vm_raise_error(vm, "CPP support is not enabled (build with -DFUN_WITH_CPP=ON)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* libsql ops (independent) */
|
/* libsql ops (independent) */
|
||||||
#ifdef FUN_WITH_LIBSQL
|
#ifdef FUN_WITH_LIBSQL
|
||||||
#include "vm/libsql/open.c"
|
#include "vm/libsql/close.c"
|
||||||
#include "vm/libsql/close.c"
|
#include "vm/libsql/exec.c"
|
||||||
#include "vm/libsql/exec.c"
|
#include "vm/libsql/open.c"
|
||||||
#include "vm/libsql/query.c"
|
#include "vm/libsql/query.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* PCRE2 ops */
|
/* PCRE2 ops */
|
||||||
#ifdef FUN_WITH_PCRE2
|
#ifdef FUN_WITH_PCRE2
|
||||||
#include "vm/pcre2/test.c"
|
#include "vm/pcre2/findall.c"
|
||||||
#include "vm/pcre2/match.c"
|
#include "vm/pcre2/match.c"
|
||||||
#include "vm/pcre2/findall.c"
|
#include "vm/pcre2/test.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "vm/strings/find.c"
|
#include "vm/strings/find.c"
|
||||||
#include "vm/strings/regex_match.c"
|
#include "vm/strings/regex_match.c"
|
||||||
#include "vm/strings/regex_search.c"
|
#include "vm/strings/regex_replace.c"
|
||||||
#include "vm/strings/regex_replace.c"
|
#include "vm/strings/regex_search.c"
|
||||||
#include "vm/strings/split.c"
|
#include "vm/strings/split.c"
|
||||||
#include "vm/strings/substr.c"
|
#include "vm/strings/substr.c"
|
||||||
|
|
||||||
#include "vm/len.c"
|
#include "vm/cast.c"
|
||||||
#include "vm/line.c"
|
#include "vm/echo.c"
|
||||||
#include "vm/print.c"
|
#include "vm/len.c"
|
||||||
#include "vm/echo.c"
|
#include "vm/line.c"
|
||||||
#include "vm/to_number.c"
|
#include "vm/os/list_dir.c"
|
||||||
#include "vm/to_string.c"
|
#include "vm/print.c"
|
||||||
#include "vm/cast.c"
|
#include "vm/sclamp.c"
|
||||||
#include "vm/typeof.c"
|
#include "vm/to_number.c"
|
||||||
#include "vm/uclamp.c"
|
#include "vm/to_string.c"
|
||||||
#include "vm/sclamp.c"
|
#include "vm/typeof.c"
|
||||||
#include "vm/os/list_dir.c"
|
#include "vm/uclamp.c"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (!opcode_is_valid(inst.op)) {
|
if (!opcode_is_valid(inst.op)) {
|
||||||
fprintf(stderr, "Runtime error: unknown opcode %d (%s) at instruction %d\n",
|
fprintf(stderr, "Runtime error: unknown opcode %d (%s) at instruction %d\n",
|
||||||
inst.op,
|
inst.op,
|
||||||
(inst.op >= 0 && inst.op < sizeof(opcode_names)/sizeof(opcode_names[0]))
|
(inst.op >= 0 && inst.op < sizeof(opcode_names) / sizeof(opcode_names[0]))
|
||||||
? opcode_names[inst.op] : "???",
|
? opcode_names[inst.op]
|
||||||
|
: "???",
|
||||||
f->ip - 1);
|
f->ip - 1);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
67
src/vm.h
67
src/vm.h
|
|
@ -20,49 +20,48 @@
|
||||||
#define STACK_SIZE 1024
|
#define STACK_SIZE 1024
|
||||||
|
|
||||||
static const char *opcode_names[] = {
|
static const char *opcode_names[] = {
|
||||||
"NOP","LOAD_CONST","LOAD_LOCAL","STORE_LOCAL",
|
"NOP", "LOAD_CONST", "LOAD_LOCAL", "STORE_LOCAL",
|
||||||
"LOAD_GLOBAL","STORE_GLOBAL","ADD","SUB","MUL","DIV",
|
"LOAD_GLOBAL", "STORE_GLOBAL", "ADD", "SUB", "MUL", "DIV",
|
||||||
"LT","LTE","GT","GTE","EQ","NEQ","POP","JUMP",
|
"LT", "LTE", "GT", "GTE", "EQ", "NEQ", "POP", "JUMP",
|
||||||
"JUMP_IF_FALSE","CALL","RETURN","PRINT","ECHO","HALT",
|
"JUMP_IF_FALSE", "CALL", "RETURN", "PRINT", "ECHO", "HALT",
|
||||||
"LINE",
|
"LINE",
|
||||||
"MOD","AND","OR","NOT","DUP","SWAP",
|
"MOD", "AND", "OR", "NOT", "DUP", "SWAP",
|
||||||
"MAKE_ARRAY","INDEX_GET","INDEX_SET",
|
"MAKE_ARRAY", "INDEX_GET", "INDEX_SET",
|
||||||
"LEN","PUSH","APOP","SET","INSERT","REMOVE","SLICE",
|
"LEN", "PUSH", "APOP", "SET", "INSERT", "REMOVE", "SLICE",
|
||||||
"TO_NUMBER","TO_STRING","CAST","TYPEOF",
|
"TO_NUMBER", "TO_STRING", "CAST", "TYPEOF",
|
||||||
"SPLIT","JOIN","SUBSTR","FIND",
|
"SPLIT", "JOIN", "SUBSTR", "FIND",
|
||||||
"REGEX_MATCH","REGEX_SEARCH","REGEX_REPLACE",
|
"REGEX_MATCH", "REGEX_SEARCH", "REGEX_REPLACE",
|
||||||
"CONTAINS","INDEX_OF","CLEAR",
|
"CONTAINS", "INDEX_OF", "CLEAR",
|
||||||
"ENUMERATE","ZIP",
|
"ENUMERATE", "ZIP",
|
||||||
"MIN","MAX","CLAMP","ABS","POW","RANDOM_SEED","RANDOM_INT",
|
"MIN", "MAX", "CLAMP", "ABS", "POW", "RANDOM_SEED", "RANDOM_INT",
|
||||||
"MAKE_MAP","KEYS","VALUES","HAS_KEY",
|
"MAKE_MAP", "KEYS", "VALUES", "HAS_KEY",
|
||||||
"READ_FILE","WRITE_FILE","ENV","INPUT_LINE","PROC_RUN","PROC_SYSTEM",
|
"READ_FILE", "WRITE_FILE", "ENV", "INPUT_LINE", "PROC_RUN", "PROC_SYSTEM",
|
||||||
"TIME_NOW_MS","CLOCK_MONO_MS","DATE_FORMAT",
|
"TIME_NOW_MS", "CLOCK_MONO_MS", "DATE_FORMAT",
|
||||||
"THREAD_SPAWN","THREAD_JOIN","SLEEP_MS",
|
"THREAD_SPAWN", "THREAD_JOIN", "SLEEP_MS",
|
||||||
"RANDOM_NUMBER",
|
"RANDOM_NUMBER",
|
||||||
"BAND","BOR","BXOR","BNOT","SHL","SHR","ROTL","ROTR",
|
"BAND", "BOR", "BXOR", "BNOT", "SHL", "SHR", "ROTL", "ROTR",
|
||||||
"JSON_PARSE","JSON_STRINGIFY","JSON_FROM_FILE","JSON_TO_FILE",
|
"JSON_PARSE", "JSON_STRINGIFY", "JSON_FROM_FILE", "JSON_TO_FILE",
|
||||||
"CURL_GET","CURL_POST","CURL_DOWNLOAD",
|
"CURL_GET", "CURL_POST", "CURL_DOWNLOAD",
|
||||||
"SQLITE_OPEN","SQLITE_CLOSE","SQLITE_EXEC","SQLITE_QUERY",
|
"SQLITE_OPEN", "SQLITE_CLOSE", "SQLITE_EXEC", "SQLITE_QUERY",
|
||||||
"LIBSQL_OPEN","LIBSQL_CLOSE","LIBSQL_EXEC","LIBSQL_QUERY",
|
"LIBSQL_OPEN", "LIBSQL_CLOSE", "LIBSQL_EXEC", "LIBSQL_QUERY",
|
||||||
"PCSC_ESTABLISH","PCSC_RELEASE","PCSC_LIST_READERS","PCSC_CONNECT","PCSC_DISCONNECT","PCSC_TRANSMIT",
|
"PCSC_ESTABLISH", "PCSC_RELEASE", "PCSC_LIST_READERS", "PCSC_CONNECT", "PCSC_DISCONNECT", "PCSC_TRANSMIT",
|
||||||
"PCRE2_TEST","PCRE2_MATCH","PCRE2_FINDALL",
|
"PCRE2_TEST", "PCRE2_MATCH", "PCRE2_FINDALL",
|
||||||
"INI_LOAD","INI_FREE","INI_GET_STRING","INI_GET_INT","INI_GET_DOUBLE","INI_GET_BOOL","INI_SET","INI_UNSET","INI_SAVE",
|
"INI_LOAD", "INI_FREE", "INI_GET_STRING", "INI_GET_INT", "INI_GET_DOUBLE", "INI_GET_BOOL", "INI_SET", "INI_UNSET", "INI_SAVE",
|
||||||
"XML_PARSE","XML_ROOT","XML_NAME","XML_TEXT",
|
"XML_PARSE", "XML_ROOT", "XML_NAME", "XML_TEXT",
|
||||||
"SOCK_TCP_LISTEN","SOCK_TCP_ACCEPT","SOCK_TCP_CONNECT","SOCK_SEND","SOCK_RECV","SOCK_CLOSE","SOCK_UNIX_LISTEN","SOCK_UNIX_CONNECT",
|
"SOCK_TCP_LISTEN", "SOCK_TCP_ACCEPT", "SOCK_TCP_CONNECT", "SOCK_SEND", "SOCK_RECV", "SOCK_CLOSE", "SOCK_UNIX_LISTEN", "SOCK_UNIX_CONNECT",
|
||||||
"EXIT",
|
"EXIT",
|
||||||
"OS_LIST_DIR",
|
"OS_LIST_DIR",
|
||||||
"TK_BIND",
|
"TK_BIND",
|
||||||
"SERIAL_OPEN","SERIAL_CONFIG","SERIAL_SEND","SERIAL_RECV","SERIAL_CLOSE",
|
"SERIAL_OPEN", "SERIAL_CONFIG", "SERIAL_SEND", "SERIAL_RECV", "SERIAL_CLOSE",
|
||||||
"TK_EVAL","TK_RESULT","TK_LOOP","TK_WM_TITLE","TK_LABEL","TK_BUTTON","TK_PACK",
|
"TK_EVAL", "TK_RESULT", "TK_LOOP", "TK_WM_TITLE", "TK_LABEL", "TK_BUTTON", "TK_PACK",
|
||||||
"TRY_PUSH","TRY_POP","THROW",
|
"TRY_PUSH", "TRY_POP", "THROW",
|
||||||
"FMIN","FMAX",
|
"FMIN", "FMAX",
|
||||||
/* Rust FFI demo */
|
/* Rust FFI demo */
|
||||||
"RUST_HELLO","RUST_HELLO_ARGS","RUST_HELLO_ARGS_RETURN","RUST_GET_SP","RUST_SET_EXIT",
|
"RUST_HELLO", "RUST_HELLO_ARGS", "RUST_HELLO_ARGS_RETURN", "RUST_GET_SP", "RUST_SET_EXIT",
|
||||||
/* C++ demo */
|
/* C++ demo */
|
||||||
"CPP_ADD",
|
"CPP_ADD",
|
||||||
/* Notcurses TUI (optional) */
|
/* Notcurses TUI (optional) */
|
||||||
"NC_INIT","NC_SHUTDOWN","NC_CLEAR","NC_DRAW_TEXT","NC_GETCH"
|
"NC_INIT", "NC_SHUTDOWN", "NC_CLEAR", "NC_DRAW_TEXT", "NC_GETCH"};
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Bytecode *fn;
|
Bytecode *fn;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file add.c
|
* @file add.c
|
||||||
* @brief Implements the OP_ADD opcode for arithmetic and string concatenation in the VM.
|
* @brief Implements the OP_ADD opcode for arithmetic and string concatenation in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_ADD instruction, which performs addition or concatenation
|
* This file handles the OP_ADD instruction, which performs addition or concatenation
|
||||||
|
|
@ -57,7 +57,7 @@ case OP_ADD: {
|
||||||
const char *sb = b.s ? b.s : "";
|
const char *sb = b.s ? b.s : "";
|
||||||
size_t la = strlen(sa);
|
size_t la = strlen(sa);
|
||||||
size_t lb = strlen(sb);
|
size_t lb = strlen(sb);
|
||||||
char *buf = (char*)malloc(la + lb + 1);
|
char *buf = (char *)malloc(la + lb + 1);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
fprintf(stderr, "Runtime error: out of memory during string concatenation\n");
|
fprintf(stderr, "Runtime error: out of memory during string concatenation\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file div.c
|
* @file div.c
|
||||||
* @brief Implements the OP_DIV opcode for integer division in the VM.
|
* @brief Implements the OP_DIV opcode for integer division in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_DIV instruction, which performs integer division
|
* This file handles the OP_DIV instruction, which performs integer division
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file mul.c
|
* @file mul.c
|
||||||
* @brief Implements the OP_MUL opcode for integer multiplication in the VM.
|
* @brief Implements the OP_MUL opcode for integer multiplication in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_MUL instruction, which performs integer multiplication
|
* This file handles the OP_MUL instruction, which performs integer multiplication
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file sub.c
|
* @file sub.c
|
||||||
* @brief Implements the OP_SUB opcode for integer subtraction in the VM.
|
* @brief Implements the OP_SUB opcode for integer subtraction in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_SUB instruction, which performs integer subtraction
|
* This file handles the OP_SUB instruction, which performs integer subtraction
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file apop.c
|
* @file apop.c
|
||||||
* @brief Implements the OP_APOP opcode for removing elements from arrays in the VM.
|
* @brief Implements the OP_APOP opcode for removing elements from arrays in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_APOP instruction, which removes the last element from an array
|
* This file handles the OP_APOP instruction, which removes the last element from an array
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file contains.c
|
* @file contains.c
|
||||||
* @brief Implements the OP_CONTAINS opcode for checking array membership in the VM.
|
* @brief Implements the OP_CONTAINS opcode for checking array membership in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_CONTAINS instruction, which checks if a value is present in an array.
|
* This file handles the OP_CONTAINS instruction, which checks if a value is present in an array.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file enumerate.c
|
* @file enumerate.c
|
||||||
* @brief Implements the OP_ENUMERATE opcode for enumerating arrays in the VM.
|
* @brief Implements the OP_ENUMERATE opcode for enumerating arrays in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_ENUMERATE instruction, which creates an array of [index, value] pairs
|
* This file handles the OP_ENUMERATE instruction, which creates an array of [index, value] pairs
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file index_get.c
|
* @file index_get.c
|
||||||
* @brief Implements the OP_INDEX_GET opcode for array and map indexing in the VM.
|
* @brief Implements the OP_INDEX_GET opcode for array and map indexing in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_INDEX_GET instruction, which retrieves an element from
|
* This file handles the OP_INDEX_GET instruction, which retrieves an element from
|
||||||
|
|
@ -41,16 +41,23 @@ case OP_INDEX_GET: {
|
||||||
container.type, idx.type);
|
container.type, idx.type);
|
||||||
#endif
|
#endif
|
||||||
if (container.type == VAL_ARRAY) {
|
if (container.type == VAL_ARRAY) {
|
||||||
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_GET index must be int for array\n"); exit(1); }
|
if (idx.type != VAL_INT) {
|
||||||
|
fprintf(stderr, "INDEX_GET index must be int for array\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
Value elem;
|
Value elem;
|
||||||
if (!array_get_copy(&container, (int)idx.i, &elem)) {
|
if (!array_get_copy(&container, (int)idx.i, &elem)) {
|
||||||
fprintf(stderr, "Runtime error: index out of range\n"); exit(1);
|
fprintf(stderr, "Runtime error: index out of range\n");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
free_value(container);
|
free_value(container);
|
||||||
free_value(idx);
|
free_value(idx);
|
||||||
push_value(vm, elem);
|
push_value(vm, elem);
|
||||||
} else if (container.type == VAL_MAP) {
|
} else if (container.type == VAL_MAP) {
|
||||||
if (idx.type != VAL_STRING) { fprintf(stderr, "INDEX_GET key must be string for map\n"); exit(1); }
|
if (idx.type != VAL_STRING) {
|
||||||
|
fprintf(stderr, "INDEX_GET key must be string for map\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
Value out;
|
Value out;
|
||||||
if (!map_get_copy(&container, idx.s ? idx.s : "", &out)) {
|
if (!map_get_copy(&container, idx.s ? idx.s : "", &out)) {
|
||||||
out = make_nil();
|
out = make_nil();
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file index_of.c
|
* @file index_of.c
|
||||||
* @brief Implements the OP_INDEX_OF opcode for finding the index of a value in an array in the VM.
|
* @brief Implements the OP_INDEX_OF opcode for finding the index of a value in an array in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_INDEX_OF instruction, which finds the index of a value in an array.
|
* This file handles the OP_INDEX_OF instruction, which finds the index of a value in an array.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file index_set.c
|
* @file index_set.c
|
||||||
* @brief Implements the OP_INDEX_SET opcode for array and map assignment in the VM.
|
* @brief Implements the OP_INDEX_SET opcode for array and map assignment in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_INDEX_SET instruction, which assigns a value to an
|
* This file handles the OP_INDEX_SET instruction, which assigns a value to an
|
||||||
|
|
@ -32,7 +32,6 @@
|
||||||
* @date 2025-10-16
|
* @date 2025-10-16
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
case OP_INDEX_SET: {
|
case OP_INDEX_SET: {
|
||||||
Value v = pop_value(vm);
|
Value v = pop_value(vm);
|
||||||
Value idx = pop_value(vm);
|
Value idx = pop_value(vm);
|
||||||
|
|
@ -42,16 +41,24 @@ case OP_INDEX_SET: {
|
||||||
container.type, idx.type, v.type);
|
container.type, idx.type, v.type);
|
||||||
#endif
|
#endif
|
||||||
if (container.type == VAL_ARRAY) {
|
if (container.type == VAL_ARRAY) {
|
||||||
if (idx.type != VAL_INT) { fprintf(stderr, "INDEX_SET index must be int for array\n"); exit(1); }
|
if (idx.type != VAL_INT) {
|
||||||
|
fprintf(stderr, "INDEX_SET index must be int for array\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
if (!array_set(&container, (int)idx.i, v)) {
|
if (!array_set(&container, (int)idx.i, v)) {
|
||||||
fprintf(stderr, "Runtime error: index out of range\n"); exit(1);
|
fprintf(stderr, "Runtime error: index out of range\n");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
free_value(container);
|
free_value(container);
|
||||||
free_value(idx);
|
free_value(idx);
|
||||||
} else if (container.type == VAL_MAP) {
|
} else if (container.type == VAL_MAP) {
|
||||||
if (idx.type != VAL_STRING) { fprintf(stderr, "INDEX_SET key must be string for map\n"); exit(1); }
|
if (idx.type != VAL_STRING) {
|
||||||
|
fprintf(stderr, "INDEX_SET key must be string for map\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
if (!map_set(&container, idx.s ? idx.s : "", v)) {
|
if (!map_set(&container, idx.s ? idx.s : "", v)) {
|
||||||
fprintf(stderr, "Runtime error: map set failed\n"); exit(1);
|
fprintf(stderr, "Runtime error: map set failed\n");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
free_value(container);
|
free_value(container);
|
||||||
free_value(idx);
|
free_value(idx);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file insert.c
|
* @file insert.c
|
||||||
* @brief Implements the OP_INSERT opcode for inserting elements into arrays in the VM.
|
* @brief Implements the OP_INSERT opcode for inserting elements into arrays in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_INSERT instruction, which inserts a value into an array
|
* This file handles the OP_INSERT instruction, which inserts a value into an array
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file join.c
|
* @file join.c
|
||||||
* @brief Implements the OP_JOIN opcode for joining array elements into a string in the VM.
|
* @brief Implements the OP_JOIN opcode for joining array elements into a string in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_JOIN instruction, which joins the elements of an array into a string
|
* This file handles the OP_JOIN instruction, which joins the elements of an array into a string
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file make_array.c
|
* @file make_array.c
|
||||||
* @brief Implements the OP_MAKE_ARRAY opcode for creating arrays in the VM.
|
* @brief Implements the OP_MAKE_ARRAY opcode for creating arrays in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_MAKE_ARRAY instruction, which pops `n` values from the stack,
|
* This file handles the OP_MAKE_ARRAY instruction, which pops `n` values from the stack,
|
||||||
|
|
@ -39,8 +39,11 @@ case OP_MAKE_ARRAY: {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
/* pop n values into temp array preserving original order */
|
/* pop n values into temp array preserving original order */
|
||||||
Value *vals = (Value*)malloc(sizeof(Value) * n);
|
Value *vals = (Value *)malloc(sizeof(Value) * n);
|
||||||
if (!vals) { fprintf(stderr, "Runtime error: OOM in MAKE_ARRAY\n"); exit(1); }
|
if (!vals) {
|
||||||
|
fprintf(stderr, "Runtime error: OOM in MAKE_ARRAY\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
for (int i = n - 1; i >= 0; --i) {
|
for (int i = n - 1; i >= 0; --i) {
|
||||||
vals[i] = pop_value(vm); /* take ownership */
|
vals[i] = pop_value(vm); /* take ownership */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file arr_push.c
|
* @file arr_push.c
|
||||||
* @brief Implements the OP_ARR_PUSH opcode for appending elements to arrays in the VM.
|
* @brief Implements the OP_ARR_PUSH opcode for appending elements to arrays in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_ARR_PUSH instruction, which appends a value to the end of an array.
|
* This file handles the OP_ARR_PUSH instruction, which appends a value to the end of an array.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file arr_remove.c
|
* @file arr_remove.c
|
||||||
* @brief Implements the OP_ARR_REMOVE opcode for removing elements from arrays in the VM.
|
* @brief Implements the OP_ARR_REMOVE opcode for removing elements from arrays in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_ARR_REMOVE instruction, which removes an element from an array
|
* This file handles the OP_ARR_REMOVE instruction, which removes an element from an array
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file arr_set.c
|
* @file arr_set.c
|
||||||
* @brief Implements the OP_ARR_SET opcode for setting elements in arrays in the VM.
|
* @brief Implements the OP_ARR_SET opcode for setting elements in arrays in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_ARR_SET instruction, which sets a value at a specified index in an array.
|
* This file handles the OP_ARR_SET instruction, which sets a value at a specified index in an array.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file slice.c
|
* @file slice.c
|
||||||
* @brief Implements the OP_SLICE opcode for array slicing in the VM.
|
* @brief Implements the OP_SLICE opcode for array slicing in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_SLICE instruction, which creates a new array containing
|
* This file handles the OP_SLICE instruction, which creates a new array containing
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file zip.c
|
* @file zip.c
|
||||||
* @brief Implements the OP_ZIP opcode for array zipping in the VM.
|
* @brief Implements the OP_ZIP opcode for array zipping in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_ZIP instruction, which combines two arrays into
|
* This file handles the OP_ZIP instruction, which combines two arrays into
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -45,12 +45,16 @@ case OP_CAST: {
|
||||||
} else if (v.type == VAL_STRING) {
|
} else if (v.type == VAL_STRING) {
|
||||||
const char *s = v.s ? v.s : "";
|
const char *s = v.s ? v.s : "";
|
||||||
const char *p = s;
|
const char *p = s;
|
||||||
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
|
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
|
||||||
|
p++;
|
||||||
char *endp = NULL;
|
char *endp = NULL;
|
||||||
long long parsed = strtoll(p, &endp, 10);
|
long long parsed = strtoll(p, &endp, 10);
|
||||||
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n')) endp++;
|
while (endp && (*endp == ' ' || *endp == '\t' || *endp == '\r' || *endp == '\n'))
|
||||||
if (endp && *endp != '\0') out = make_int(0);
|
endp++;
|
||||||
else out = make_int((int64_t)parsed);
|
if (endp && *endp != '\0')
|
||||||
|
out = make_int(0);
|
||||||
|
else
|
||||||
|
out = make_int((int64_t)parsed);
|
||||||
} else {
|
} else {
|
||||||
out = make_int(0);
|
out = make_int(0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file call.c
|
* @file call.c
|
||||||
* @brief Implements the OP_CALL opcode for function calls in the VM.
|
* @brief Implements the OP_CALL opcode for function calls in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_CALL instruction, which calls a function with arguments.
|
* This file handles the OP_CALL instruction, which calls a function with arguments.
|
||||||
|
|
@ -33,7 +33,7 @@ case OP_CALL: {
|
||||||
/* collect args in reverse (preserve order) */
|
/* collect args in reverse (preserve order) */
|
||||||
Value *args = NULL;
|
Value *args = NULL;
|
||||||
if (argc > 0) {
|
if (argc > 0) {
|
||||||
args = (Value*)malloc(sizeof(Value) * argc);
|
args = (Value *)malloc(sizeof(Value) * argc);
|
||||||
/* pop args into array in reverse */
|
/* pop args into array in reverse */
|
||||||
for (int i = argc - 1; i >= 0; --i) {
|
for (int i = argc - 1; i >= 0; --i) {
|
||||||
args[i] = pop_value(vm);
|
args[i] = pop_value(vm);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file dup.c
|
* @file dup.c
|
||||||
* @brief Implements the OP_DUP opcode for duplicating the top stack value in the VM.
|
* @brief Implements the OP_DUP opcode for duplicating the top stack value in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_DUP instruction, which duplicates the top value on the stack.
|
* This file handles the OP_DUP instruction, which duplicates the top value on the stack.
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
* // Bytecode: OP_DUP
|
* // Bytecode: OP_DUP
|
||||||
* // Stack before: [42]
|
* // Stack before: [42]
|
||||||
* // Stack after: [42, 42]
|
* // Stack after: [42, 42]
|
||||||
*
|
*
|
||||||
* @author Johannes Findeisen
|
* @author Johannes Findeisen
|
||||||
* @date 2025-10-16
|
* @date 2025-10-16
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file exit.c
|
* @file exit.c
|
||||||
* @brief Implements the OP_EXIT opcode to terminate the script with an exit code.
|
* @brief Implements the OP_EXIT opcode to terminate the script with an exit code.
|
||||||
*
|
*
|
||||||
* Behavior:
|
* Behavior:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file halt.c
|
* @file halt.c
|
||||||
* @brief Implements the OP_HALT opcode for stopping VM execution.
|
* @brief Implements the OP_HALT opcode for stopping VM execution.
|
||||||
*
|
*
|
||||||
* This file handles the OP_HALT instruction, which stops the execution of the VM.
|
* This file handles the OP_HALT instruction, which stops the execution of the VM.
|
||||||
|
|
@ -27,4 +27,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
case OP_HALT:
|
case OP_HALT:
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file jump.c
|
* @file jump.c
|
||||||
* @brief Implements the OP_JUMP opcode for unconditional jumps in the VM.
|
* @brief Implements the OP_JUMP opcode for unconditional jumps in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_JUMP instruction, which performs an unconditional
|
* This file handles the OP_JUMP instruction, which performs an unconditional
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file jump_if_false.c
|
* @file jump_if_false.c
|
||||||
* @brief Implements the OP_JUMP_IF_FALSE opcode for conditional jumps in the VM.
|
* @brief Implements the OP_JUMP_IF_FALSE opcode for conditional jumps in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_JUMP_IF_FALSE instruction, which jumps if the top
|
* This file handles the OP_JUMP_IF_FALSE instruction, which jumps if the top
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file load_const.c
|
* @file load_const.c
|
||||||
* @brief Implements the OP_LOAD_CONST opcode for loading constants in the VM.
|
* @brief Implements the OP_LOAD_CONST opcode for loading constants in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_LOAD_CONST instruction, which loads a constant value
|
* This file handles the OP_LOAD_CONST instruction, which loads a constant value
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file load_global.c
|
* @file load_global.c
|
||||||
* @brief Implements the OP_LOAD_GLOBAL opcode for loading global variables in the VM.
|
* @brief Implements the OP_LOAD_GLOBAL opcode for loading global variables in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_LOAD_GLOBAL instruction, which loads a global variable
|
* This file handles the OP_LOAD_GLOBAL instruction, which loads a global variable
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file load_local.c
|
* @file load_local.c
|
||||||
* @brief Implements the OP_LOAD_LOCAL opcode for loading local variables in the VM.
|
* @brief Implements the OP_LOAD_LOCAL opcode for loading local variables in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_LOAD_LOCAL instruction, which loads a local variable
|
* This file handles the OP_LOAD_LOCAL instruction, which loads a local variable
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file nop.c
|
* @file nop.c
|
||||||
* @brief Implements the OP_NOP opcode for no operation in the VM.
|
* @brief Implements the OP_NOP opcode for no operation in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_NOP instruction, which performs no operation.
|
* This file handles the OP_NOP instruction, which performs no operation.
|
||||||
|
|
@ -27,4 +27,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
case OP_NOP:
|
case OP_NOP:
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file pop.c
|
* @file pop.c
|
||||||
* @brief Implements the OP_POP opcode for removing the top stack value in the VM.
|
* @brief Implements the OP_POP opcode for removing the top stack value in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_POP instruction, which removes the top value from the stack.
|
* This file handles the OP_POP instruction, which removes the top value from the stack.
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file return.c
|
* @file return.c
|
||||||
* @brief Implements the OP_RETURN opcode for returning from a function in the VM.
|
* @brief Implements the OP_RETURN opcode for returning from a function in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_RETURN instruction, which returns from the current function
|
* This file handles the OP_RETURN instruction, which returns from the current function
|
||||||
|
|
@ -33,8 +33,10 @@
|
||||||
|
|
||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
Value retv;
|
Value retv;
|
||||||
if (vm->sp >= 0) retv = pop_value(vm);
|
if (vm->sp >= 0)
|
||||||
else retv = make_nil();
|
retv = pop_value(vm);
|
||||||
|
else
|
||||||
|
retv = make_nil();
|
||||||
vm_pop_frame(vm);
|
vm_pop_frame(vm);
|
||||||
push_value(vm, retv);
|
push_value(vm, retv);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file store_global.c
|
* @file store_global.c
|
||||||
* @brief Implements the OP_STORE_GLOBAL opcode for storing global variables in the VM.
|
* @brief Implements the OP_STORE_GLOBAL opcode for storing global variables in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_STORE_GLOBAL instruction, which stores a value into a global variable
|
* This file handles the OP_STORE_GLOBAL instruction, which stores a value into a global variable
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file store_local.c
|
* @file store_local.c
|
||||||
* @brief Implements the OP_STORE_LOCAL opcode for storing local variables in the VM.
|
* @brief Implements the OP_STORE_LOCAL opcode for storing local variables in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_STORE_LOCAL instruction, which stores a value into a local variable
|
* This file handles the OP_STORE_LOCAL instruction, which stores a value into a local variable
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file swap.c
|
* @file swap.c
|
||||||
* @brief Implements the OP_SWAP opcode for stack manipulation in the VM.
|
* @brief Implements the OP_SWAP opcode for stack manipulation in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_SWAP instruction, which swaps the top two values
|
* This file handles the OP_SWAP instruction, which swaps the top two values
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
case OP_TRY_PUSH: {
|
case OP_TRY_PUSH: {
|
||||||
/* push index of this TRY instruction; handler ip is in its operand (may be patched later) */
|
/* push index of this TRY instruction; handler ip is in its operand (may be patched later) */
|
||||||
if (f->try_sp >= (int)(sizeof(f->try_stack)/sizeof(f->try_stack[0])) - 1) {
|
if (f->try_sp >= (int)(sizeof(f->try_stack) / sizeof(f->try_stack[0])) - 1) {
|
||||||
fprintf(stderr, "Runtime error: try depth exceeded\n");
|
fprintf(stderr, "Runtime error: try depth exceeded\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
* Copyright 2026 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,17 @@ case OP_CURL_GET: {
|
||||||
Value vurl = pop_value(vm);
|
Value vurl = pop_value(vm);
|
||||||
char *url = value_to_string_alloc(&vurl);
|
char *url = value_to_string_alloc(&vurl);
|
||||||
free_value(vurl);
|
free_value(vurl);
|
||||||
if (!url) { push_value(vm, make_string("")); break; }
|
if (!url) {
|
||||||
FunCurlBuf buf = { NULL, 0 };
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FunCurlBuf buf = {NULL, 0};
|
||||||
CURL *h = curl_easy_init();
|
CURL *h = curl_easy_init();
|
||||||
if (!h) { free(url); push_value(vm, make_string("")); break; }
|
if (!h) {
|
||||||
|
free(url);
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
curl_easy_setopt(h, CURLOPT_URL, url);
|
curl_easy_setopt(h, CURLOPT_URL, url);
|
||||||
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
|
curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
|
curl_easy_setopt(h, CURLOPT_WRITEFUNCTION, fun_curl_write_cb);
|
||||||
|
|
@ -26,7 +33,8 @@ case OP_CURL_GET: {
|
||||||
if (buf.d) free(buf.d);
|
if (buf.d) free(buf.d);
|
||||||
push_value(vm, s);
|
push_value(vm, s);
|
||||||
#else
|
#else
|
||||||
Value v = pop_value(vm); free_value(v);
|
Value v = pop_value(vm);
|
||||||
|
free_value(v);
|
||||||
push_value(vm, make_string(""));
|
push_value(vm, make_string(""));
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
* https://opensource.org/license/apache-2-0
|
* https://opensource.org/license/apache-2-0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements OP_ECHO: print top-of-stack value without trailing newline.
|
* Implements OP_ECHO: print top-of-stack value without trailing newline.
|
||||||
* Now stores the value into the VM's output buffer and marks it as partial,
|
* Now stores the value into the VM's output buffer and marks it as partial,
|
||||||
* so the CLI can render echo output together with following print output.
|
* so the CLI can render echo output together with following print output.
|
||||||
|
|
|
||||||
|
|
@ -16,46 +16,62 @@ case OP_INI_GET_BOOL: {
|
||||||
Value vkey = pop_value(vm);
|
Value vkey = pop_value(vm);
|
||||||
Value vsec = pop_value(vm);
|
Value vsec = pop_value(vm);
|
||||||
Value vh = pop_value(vm);
|
Value vh = pop_value(vm);
|
||||||
int def = (vdef.type==VAL_INT||vdef.type==VAL_BOOL) ? (int)vdef.i : 0;
|
int def = (vdef.type == VAL_INT || vdef.type == VAL_BOOL) ? (int)vdef.i : 0;
|
||||||
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
|
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
|
||||||
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
|
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
|
||||||
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
|
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
int outb = def;
|
int outb = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; char alt[1024];
|
char full[1024];
|
||||||
|
char alt[1024];
|
||||||
ini_make_full_key(full, sizeof(full), sec, key);
|
ini_make_full_key(full, sizeof(full), sec, key);
|
||||||
memcpy(alt, full, sizeof(alt));
|
memcpy(alt, full, sizeof(alt));
|
||||||
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
|
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
|
||||||
|
if (alt[i] == ':') {
|
||||||
|
alt[i] = '.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
const char *s = iniparser_getstring(d, full, NULL);
|
const char *s = iniparser_getstring(d, full, NULL);
|
||||||
if (!s) s = iniparser_getstring(d, alt, NULL);
|
if (!s) s = iniparser_getstring(d, alt, NULL);
|
||||||
if (s) {
|
if (s) {
|
||||||
/* normalize and parse boolean */
|
/* normalize and parse boolean */
|
||||||
char buf[256];
|
char buf[256];
|
||||||
size_t n = strlen(s);
|
size_t n = strlen(s);
|
||||||
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
|
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
|
||||||
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
|
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
|
||||||
memcpy(buf, s+1, copy); buf[copy] = '\0';
|
memcpy(buf, s + 1, copy);
|
||||||
|
buf[copy] = '\0';
|
||||||
s = buf;
|
s = buf;
|
||||||
}
|
}
|
||||||
/* trim spaces */
|
/* trim spaces */
|
||||||
while (*s && (unsigned char)*s <= ' ') s++;
|
while (*s && (unsigned char)*s <= ' ')
|
||||||
|
s++;
|
||||||
/* lower copy for textual booleans */
|
/* lower copy for textual booleans */
|
||||||
char lb[256]; size_t li=0; for (; s[li] && li < sizeof(lb)-1; ++li) lb[li] = (char)tolower((unsigned char)s[li]); lb[li]='\0';
|
char lb[256];
|
||||||
if (strcmp(lb, "true")==0 || strcmp(lb, "yes")==0 || strcmp(lb, "on")==0) {
|
size_t li = 0;
|
||||||
|
for (; s[li] && li < sizeof(lb) - 1; ++li)
|
||||||
|
lb[li] = (char)tolower((unsigned char)s[li]);
|
||||||
|
lb[li] = '\0';
|
||||||
|
if (strcmp(lb, "true") == 0 || strcmp(lb, "yes") == 0 || strcmp(lb, "on") == 0) {
|
||||||
outb = 1;
|
outb = 1;
|
||||||
} else if (strcmp(lb, "false")==0 || strcmp(lb, "no")==0 || strcmp(lb, "off")==0) {
|
} else if (strcmp(lb, "false") == 0 || strcmp(lb, "no") == 0 || strcmp(lb, "off") == 0) {
|
||||||
outb = 0;
|
outb = 0;
|
||||||
} else {
|
} else {
|
||||||
/* numeric */
|
/* numeric */
|
||||||
char *endp=NULL; long v = strtol(lb, &endp, 10);
|
char *endp = NULL;
|
||||||
outb = (endp && endp!=lb) ? (v!=0) : def;
|
long v = strtol(lb, &endp, 10);
|
||||||
|
outb = (endp && endp != lb) ? (v != 0) : def;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outb = def;
|
outb = def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef);
|
||||||
|
free_value(vkey);
|
||||||
|
free_value(vsec);
|
||||||
|
free_value(vh);
|
||||||
push_value(vm, make_int(outb ? 1 : 0));
|
push_value(vm, make_int(outb ? 1 : 0));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,36 +16,50 @@ case OP_INI_GET_DOUBLE: {
|
||||||
Value vkey = pop_value(vm);
|
Value vkey = pop_value(vm);
|
||||||
Value vsec = pop_value(vm);
|
Value vsec = pop_value(vm);
|
||||||
Value vh = pop_value(vm);
|
Value vh = pop_value(vm);
|
||||||
double def = (vdef.type==VAL_FLOAT) ? vdef.d : (vdef.type==VAL_INT ? (double)vdef.i : 0.0);
|
double def = (vdef.type == VAL_FLOAT) ? vdef.d : (vdef.type == VAL_INT ? (double)vdef.i : 0.0);
|
||||||
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
|
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
|
||||||
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
|
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
|
||||||
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
|
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
double outd = def;
|
double outd = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; char alt[1024];
|
char full[1024];
|
||||||
|
char alt[1024];
|
||||||
ini_make_full_key(full, sizeof(full), sec, key);
|
ini_make_full_key(full, sizeof(full), sec, key);
|
||||||
memcpy(alt, full, sizeof(alt));
|
memcpy(alt, full, sizeof(alt));
|
||||||
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
|
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
|
||||||
|
if (alt[i] == ':') {
|
||||||
|
alt[i] = '.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
const char *s = iniparser_getstring(d, full, NULL);
|
const char *s = iniparser_getstring(d, full, NULL);
|
||||||
if (!s) s = iniparser_getstring(d, alt, NULL);
|
if (!s) s = iniparser_getstring(d, alt, NULL);
|
||||||
if (s) {
|
if (s) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
size_t n = strlen(s);
|
size_t n = strlen(s);
|
||||||
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
|
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
|
||||||
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
|
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
|
||||||
memcpy(buf, s+1, copy); buf[copy] = '\0';
|
memcpy(buf, s + 1, copy);
|
||||||
|
buf[copy] = '\0';
|
||||||
s = buf;
|
s = buf;
|
||||||
}
|
}
|
||||||
while (*s && (unsigned char)*s <= ' ') s++;
|
while (*s && (unsigned char)*s <= ' ')
|
||||||
|
s++;
|
||||||
char *endp = NULL;
|
char *endp = NULL;
|
||||||
double v = strtod(s, &endp);
|
double v = strtod(s, &endp);
|
||||||
if (endp && endp != s) outd = v; else outd = def;
|
if (endp && endp != s)
|
||||||
|
outd = v;
|
||||||
|
else
|
||||||
|
outd = def;
|
||||||
} else {
|
} else {
|
||||||
outd = def;
|
outd = def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef);
|
||||||
|
free_value(vkey);
|
||||||
|
free_value(vsec);
|
||||||
|
free_value(vh);
|
||||||
push_value(vm, make_float(outd));
|
push_value(vm, make_float(outd));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,38 +16,52 @@ case OP_INI_GET_INT: {
|
||||||
Value vkey = pop_value(vm);
|
Value vkey = pop_value(vm);
|
||||||
Value vsec = pop_value(vm);
|
Value vsec = pop_value(vm);
|
||||||
Value vh = pop_value(vm);
|
Value vh = pop_value(vm);
|
||||||
int def = (vdef.type==VAL_INT) ? (int)vdef.i : 0;
|
int def = (vdef.type == VAL_INT) ? (int)vdef.i : 0;
|
||||||
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
|
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
|
||||||
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
|
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
|
||||||
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
|
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
int outi = def;
|
int outi = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; char alt[1024];
|
char full[1024];
|
||||||
|
char alt[1024];
|
||||||
ini_make_full_key(full, sizeof(full), sec, key);
|
ini_make_full_key(full, sizeof(full), sec, key);
|
||||||
memcpy(alt, full, sizeof(alt));
|
memcpy(alt, full, sizeof(alt));
|
||||||
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
|
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
|
||||||
|
if (alt[i] == ':') {
|
||||||
|
alt[i] = '.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
const char *s = iniparser_getstring(d, full, NULL);
|
const char *s = iniparser_getstring(d, full, NULL);
|
||||||
if (!s) s = iniparser_getstring(d, alt, NULL);
|
if (!s) s = iniparser_getstring(d, alt, NULL);
|
||||||
if (s) {
|
if (s) {
|
||||||
/* strip optional quotes and parse */
|
/* strip optional quotes and parse */
|
||||||
char buf[256];
|
char buf[256];
|
||||||
size_t n = strlen(s);
|
size_t n = strlen(s);
|
||||||
if (n >= 2 && ((s[0]=='"' && s[n-1]=='"') || (s[0]=='\'' && s[n-1]=='\''))) {
|
if (n >= 2 && ((s[0] == '"' && s[n - 1] == '"') || (s[0] == '\'' && s[n - 1] == '\''))) {
|
||||||
size_t copy = (n-2) < sizeof(buf)-1 ? (n-2) : sizeof(buf)-1;
|
size_t copy = (n - 2) < sizeof(buf) - 1 ? (n - 2) : sizeof(buf) - 1;
|
||||||
memcpy(buf, s+1, copy); buf[copy] = '\0';
|
memcpy(buf, s + 1, copy);
|
||||||
|
buf[copy] = '\0';
|
||||||
s = buf;
|
s = buf;
|
||||||
}
|
}
|
||||||
/* skip leading spaces */
|
/* skip leading spaces */
|
||||||
while (*s && (unsigned char)*s <= ' ') s++;
|
while (*s && (unsigned char)*s <= ' ')
|
||||||
|
s++;
|
||||||
char *endp = NULL;
|
char *endp = NULL;
|
||||||
long v = strtol(s, &endp, 10);
|
long v = strtol(s, &endp, 10);
|
||||||
if (endp && endp != s) outi = (int)v; else outi = def;
|
if (endp && endp != s)
|
||||||
|
outi = (int)v;
|
||||||
|
else
|
||||||
|
outi = def;
|
||||||
} else {
|
} else {
|
||||||
outi = def;
|
outi = def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef);
|
||||||
|
free_value(vkey);
|
||||||
|
free_value(vsec);
|
||||||
|
free_value(vh);
|
||||||
push_value(vm, make_int(outi));
|
push_value(vm, make_int(outi));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,21 +16,26 @@ case OP_INI_GET_STRING: {
|
||||||
Value vkey = pop_value(vm);
|
Value vkey = pop_value(vm);
|
||||||
Value vsec = pop_value(vm);
|
Value vsec = pop_value(vm);
|
||||||
Value vh = pop_value(vm);
|
Value vh = pop_value(vm);
|
||||||
const char *def = (vdef.type==VAL_STRING && vdef.s) ? vdef.s : "";
|
const char *def = (vdef.type == VAL_STRING && vdef.s) ? vdef.s : "";
|
||||||
const char *key = (vkey.type==VAL_STRING) ? vkey.s : NULL;
|
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
|
||||||
const char *sec = (vsec.type==VAL_STRING) ? vsec.s : NULL;
|
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
|
||||||
int h = (vh.type==VAL_INT) ? (int)vh.i : 0;
|
int h = (vh.type == VAL_INT) ? (int)vh.i : 0;
|
||||||
dictionary *d = ini_get(h);
|
dictionary *d = ini_get(h);
|
||||||
const char *res = def;
|
const char *res = def;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; char alt[1024];
|
char full[1024];
|
||||||
|
char alt[1024];
|
||||||
ini_make_full_key(full, sizeof(full), sec, key);
|
ini_make_full_key(full, sizeof(full), sec, key);
|
||||||
/* Build alternate with dot separator for robustness */
|
/* Build alternate with dot separator for robustness */
|
||||||
ini_make_full_key(alt, sizeof(alt), sec, key);
|
ini_make_full_key(alt, sizeof(alt), sec, key);
|
||||||
size_t flen = strlen(full);
|
size_t flen = strlen(full);
|
||||||
if (flen < sizeof(alt) && flen > 0) { /* create dot version in alt */
|
if (flen < sizeof(alt) && flen > 0) { /* create dot version in alt */
|
||||||
memcpy(alt, full, flen + 1);
|
memcpy(alt, full, flen + 1);
|
||||||
for (size_t i = 0; i < flen; ++i) if (alt[i] == ':') { alt[i] = '.'; break; }
|
for (size_t i = 0; i < flen; ++i)
|
||||||
|
if (alt[i] == ':') {
|
||||||
|
alt[i] = '.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const char *s = iniparser_getstring(d, full, def);
|
const char *s = iniparser_getstring(d, full, def);
|
||||||
if (s == def) { /* not found, try alternate dot form */
|
if (s == def) { /* not found, try alternate dot form */
|
||||||
|
|
@ -38,7 +43,10 @@ case OP_INI_GET_STRING: {
|
||||||
}
|
}
|
||||||
res = s ? s : "";
|
res = s ? s : "";
|
||||||
}
|
}
|
||||||
free_value(vdef); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vdef);
|
||||||
|
free_value(vkey);
|
||||||
|
free_value(vsec);
|
||||||
|
free_value(vh);
|
||||||
push_value(vm, make_string(res));
|
push_value(vm, make_string(res));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,23 +9,23 @@
|
||||||
|
|
||||||
#ifdef FUN_WITH_INI
|
#ifdef FUN_WITH_INI
|
||||||
#if defined(__has_include)
|
#if defined(__has_include)
|
||||||
# if __has_include(<iniparser/iniparser.h>)
|
#if __has_include(<iniparser/iniparser.h>)
|
||||||
# include <iniparser/iniparser.h>
|
#include <iniparser/dictionary.h>
|
||||||
# include <iniparser/dictionary.h>
|
#include <iniparser/iniparser.h>
|
||||||
# elif __has_include(<iniparser.h>)
|
#elif __has_include(<iniparser.h>)
|
||||||
# include <iniparser.h>
|
#include <dictionary.h>
|
||||||
# include <dictionary.h>
|
#include <iniparser.h>
|
||||||
# else
|
|
||||||
# error "iniparser headers not found"
|
|
||||||
# endif
|
|
||||||
#else
|
#else
|
||||||
# include <iniparser/iniparser.h>
|
#error "iniparser headers not found"
|
||||||
# include <iniparser/dictionary.h>
|
#endif
|
||||||
|
#else
|
||||||
|
#include <iniparser/dictionary.h>
|
||||||
|
#include <iniparser/iniparser.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "handles.h"
|
#include "handles.h"
|
||||||
|
|
||||||
|
|
@ -33,19 +33,23 @@ IniSlot g_ini[64];
|
||||||
|
|
||||||
int ini_alloc_handle(dictionary *d) {
|
int ini_alloc_handle(dictionary *d) {
|
||||||
if (!d) return 0;
|
if (!d) return 0;
|
||||||
for (int i = 1; i < (int)(sizeof(g_ini)/sizeof(g_ini[0])); ++i) {
|
for (int i = 1; i < (int)(sizeof(g_ini) / sizeof(g_ini[0])); ++i) {
|
||||||
if (!g_ini[i].in_use) { g_ini[i].in_use = 1; g_ini[i].dict = d; return i; }
|
if (!g_ini[i].in_use) {
|
||||||
|
g_ini[i].in_use = 1;
|
||||||
|
g_ini[i].dict = d;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dictionary* ini_get(int h) {
|
dictionary *ini_get(int h) {
|
||||||
if (h > 0 && h < (int)(sizeof(g_ini)/sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
|
if (h > 0 && h < (int)(sizeof(g_ini) / sizeof(g_ini[0])) && g_ini[h].in_use) return g_ini[h].dict;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ini_free_handle(int h) {
|
int ini_free_handle(int h) {
|
||||||
if (h <= 0 || h >= (int)(sizeof(g_ini)/sizeof(g_ini[0])) || !g_ini[h].in_use) return 0;
|
if (h <= 0 || h >= (int)(sizeof(g_ini) / sizeof(g_ini[0])) || !g_ini[h].in_use) return 0;
|
||||||
if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
|
if (g_ini[h].dict) iniparser_freedict(g_ini[h].dict);
|
||||||
g_ini[h].dict = NULL;
|
g_ini[h].dict = NULL;
|
||||||
g_ini[h].in_use = 0;
|
g_ini[h].in_use = 0;
|
||||||
|
|
|
||||||
|
|
@ -14,29 +14,32 @@
|
||||||
|
|
||||||
#ifdef FUN_WITH_INI
|
#ifdef FUN_WITH_INI
|
||||||
#if defined(__has_include)
|
#if defined(__has_include)
|
||||||
# if __has_include(<iniparser/iniparser.h>)
|
#if __has_include(<iniparser/iniparser.h>)
|
||||||
# include <iniparser/iniparser.h>
|
#include <iniparser/dictionary.h>
|
||||||
# include <iniparser/dictionary.h>
|
#include <iniparser/iniparser.h>
|
||||||
# elif __has_include(<iniparser.h>)
|
#elif __has_include(<iniparser.h>)
|
||||||
# include <iniparser.h>
|
#include <dictionary.h>
|
||||||
# include <dictionary.h>
|
#include <iniparser.h>
|
||||||
# else
|
|
||||||
# error "iniparser headers not found"
|
|
||||||
# endif
|
|
||||||
#else
|
#else
|
||||||
# include <iniparser/iniparser.h>
|
#error "iniparser headers not found"
|
||||||
# include <iniparser/dictionary.h>
|
#endif
|
||||||
|
#else
|
||||||
|
#include <iniparser/dictionary.h>
|
||||||
|
#include <iniparser/iniparser.h>
|
||||||
#endif
|
#endif
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
typedef struct { dictionary *dict; int in_use; } IniSlot;
|
typedef struct {
|
||||||
|
dictionary *dict;
|
||||||
|
int in_use;
|
||||||
|
} IniSlot;
|
||||||
|
|
||||||
/* Single global registry (defined in handles.c) */
|
/* Single global registry (defined in handles.c) */
|
||||||
extern IniSlot g_ini[64];
|
extern IniSlot g_ini[64];
|
||||||
|
|
||||||
/* Registry API (implemented in handles.c) */
|
/* Registry API (implemented in handles.c) */
|
||||||
int ini_alloc_handle(dictionary *d);
|
int ini_alloc_handle(dictionary *d);
|
||||||
dictionary* ini_get(int h);
|
dictionary *ini_get(int h);
|
||||||
int ini_free_handle(int h);
|
int ini_free_handle(int h);
|
||||||
|
|
||||||
/* Helper to build section:key string safely into provided buffer (implemented in handles.c) */
|
/* Helper to build section:key string safely into provided buffer (implemented in handles.c) */
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,19 @@
|
||||||
case OP_INI_SAVE: {
|
case OP_INI_SAVE: {
|
||||||
Value vpath = pop_value(vm);
|
Value vpath = pop_value(vm);
|
||||||
Value vh = pop_value(vm);
|
Value vh = pop_value(vm);
|
||||||
const char *path = (vpath.type==VAL_STRING)?vpath.s:NULL;
|
const char *path = (vpath.type == VAL_STRING) ? vpath.s : NULL;
|
||||||
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0);
|
dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
if (d && path) {
|
if (d && path) {
|
||||||
FILE *f = fopen(path, "w");
|
FILE *f = fopen(path, "w");
|
||||||
if (f) { iniparser_dump_ini(d, f); fclose(f); ok = 1; }
|
if (f) {
|
||||||
|
iniparser_dump_ini(d, f);
|
||||||
|
fclose(f);
|
||||||
|
ok = 1;
|
||||||
}
|
}
|
||||||
free_value(vpath); free_value(vh);
|
}
|
||||||
|
free_value(vpath);
|
||||||
|
free_value(vh);
|
||||||
push_value(vm, make_int(ok));
|
push_value(vm, make_int(ok));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,17 +16,23 @@ case OP_INI_SET: {
|
||||||
Value vkey = pop_value(vm);
|
Value vkey = pop_value(vm);
|
||||||
Value vsec = pop_value(vm);
|
Value vsec = pop_value(vm);
|
||||||
Value vh = pop_value(vm);
|
Value vh = pop_value(vm);
|
||||||
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0);
|
dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
|
||||||
const char *key = (vkey.type==VAL_STRING)?vkey.s:NULL;
|
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
|
||||||
const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL;
|
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char *valstr = value_to_string_alloc(&vval);
|
char *valstr = value_to_string_alloc(&vval);
|
||||||
if (valstr) {
|
if (valstr) {
|
||||||
char full[1024]; char alt[1024];
|
char full[1024];
|
||||||
|
char alt[1024];
|
||||||
ini_make_full_key(full, sizeof(full), sec, key);
|
ini_make_full_key(full, sizeof(full), sec, key);
|
||||||
memcpy(alt, full, sizeof(alt));
|
memcpy(alt, full, sizeof(alt));
|
||||||
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
|
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
|
||||||
|
if (alt[i] == ':') {
|
||||||
|
alt[i] = '.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* iniparser 4.x does not expose iniparser_set; use dictionary_set */
|
/* iniparser 4.x does not expose iniparser_set; use dictionary_set */
|
||||||
if (dictionary_set(d, full, valstr) == 0) {
|
if (dictionary_set(d, full, valstr) == 0) {
|
||||||
ok = 1; /* 0 means success */
|
ok = 1; /* 0 means success */
|
||||||
|
|
@ -36,7 +42,10 @@ case OP_INI_SET: {
|
||||||
free(valstr);
|
free(valstr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free_value(vval); free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vval);
|
||||||
|
free_value(vkey);
|
||||||
|
free_value(vsec);
|
||||||
|
free_value(vh);
|
||||||
push_value(vm, make_int(ok));
|
push_value(vm, make_int(ok));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,29 @@ case OP_INI_UNSET: {
|
||||||
Value vkey = pop_value(vm);
|
Value vkey = pop_value(vm);
|
||||||
Value vsec = pop_value(vm);
|
Value vsec = pop_value(vm);
|
||||||
Value vh = pop_value(vm);
|
Value vh = pop_value(vm);
|
||||||
dictionary *d = ini_get((vh.type==VAL_INT)?(int)vh.i:0);
|
dictionary *d = ini_get((vh.type == VAL_INT) ? (int)vh.i : 0);
|
||||||
const char *key = (vkey.type==VAL_STRING)?vkey.s:NULL;
|
const char *key = (vkey.type == VAL_STRING) ? vkey.s : NULL;
|
||||||
const char *sec = (vsec.type==VAL_STRING)?vsec.s:NULL;
|
const char *sec = (vsec.type == VAL_STRING) ? vsec.s : NULL;
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
if (d && sec && key) {
|
if (d && sec && key) {
|
||||||
char full[1024]; char alt[1024];
|
char full[1024];
|
||||||
|
char alt[1024];
|
||||||
ini_make_full_key(full, sizeof(full), sec, key);
|
ini_make_full_key(full, sizeof(full), sec, key);
|
||||||
memcpy(alt, full, sizeof(alt));
|
memcpy(alt, full, sizeof(alt));
|
||||||
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) { if (alt[i] == ':') { alt[i] = '.'; break; } }
|
for (size_t i = 0; i < sizeof(alt) && alt[i]; ++i) {
|
||||||
|
if (alt[i] == ':') {
|
||||||
|
alt[i] = '.';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
/* iniparser 4.2.6 dictionary_unset returns void; remove both forms */
|
/* iniparser 4.2.6 dictionary_unset returns void; remove both forms */
|
||||||
dictionary_unset(d, full);
|
dictionary_unset(d, full);
|
||||||
dictionary_unset(d, alt);
|
dictionary_unset(d, alt);
|
||||||
ok = 1;
|
ok = 1;
|
||||||
}
|
}
|
||||||
free_value(vkey); free_value(vsec); free_value(vh);
|
free_value(vkey);
|
||||||
|
free_value(vsec);
|
||||||
|
free_value(vh);
|
||||||
push_value(vm, make_int(ok));
|
push_value(vm, make_int(ok));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ case OP_INPUT_LINE: {
|
||||||
/* read a line from stdin, dynamically grow buffer */
|
/* read a line from stdin, dynamically grow buffer */
|
||||||
size_t cap = 128;
|
size_t cap = 128;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
char *buf = (char*)malloc(cap);
|
char *buf = (char *)malloc(cap);
|
||||||
if (!buf) {
|
if (!buf) {
|
||||||
fprintf(stderr, "Runtime error: out of memory reading input");
|
fprintf(stderr, "Runtime error: out of memory reading input");
|
||||||
push_value(vm, make_string(""));
|
push_value(vm, make_string(""));
|
||||||
|
|
@ -72,7 +72,7 @@ case OP_INPUT_LINE: {
|
||||||
}
|
}
|
||||||
if (len + 1 >= cap) {
|
if (len + 1 >= cap) {
|
||||||
cap *= 2;
|
cap *= 2;
|
||||||
char *nb = (char*)realloc(buf, cap);
|
char *nb = (char *)realloc(buf, cap);
|
||||||
if (!nb) {
|
if (!nb) {
|
||||||
free(buf);
|
free(buf);
|
||||||
fprintf(stderr, "Runtime error: out of memory reading input");
|
fprintf(stderr, "Runtime error: out of memory reading input");
|
||||||
|
|
@ -86,7 +86,7 @@ case OP_INPUT_LINE: {
|
||||||
|
|
||||||
/* null-terminate */
|
/* null-terminate */
|
||||||
if (len + 1 >= cap) {
|
if (len + 1 >= cap) {
|
||||||
char *nb = (char*)realloc(buf, len + 1);
|
char *nb = (char *)realloc(buf, len + 1);
|
||||||
if (!nb) {
|
if (!nb) {
|
||||||
free(buf);
|
free(buf);
|
||||||
fprintf(stderr, "Runtime error: out of memory finalizing input");
|
fprintf(stderr, "Runtime error: out of memory finalizing input");
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file read_file.c
|
* @file read_file.c
|
||||||
* @brief Implements the OP_READ_FILE opcode for reading file contents in the VM.
|
* @brief Implements the OP_READ_FILE opcode for reading file contents in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_READ_FILE instruction, which reads the contents of a file
|
* This file handles the OP_READ_FILE instruction, which reads the contents of a file
|
||||||
|
|
@ -33,18 +33,39 @@
|
||||||
|
|
||||||
case OP_READ_FILE: {
|
case OP_READ_FILE: {
|
||||||
Value path = pop_value(vm);
|
Value path = pop_value(vm);
|
||||||
if (path.type != VAL_STRING) { fprintf(stderr, "READ_FILE expects string\n"); exit(1); }
|
if (path.type != VAL_STRING) {
|
||||||
|
fprintf(stderr, "READ_FILE expects string\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
const char *p = path.s ? path.s : "";
|
const char *p = path.s ? path.s : "";
|
||||||
FILE *f = fopen(p, "rb");
|
FILE *f = fopen(p, "rb");
|
||||||
if (!f) { free_value(path); push_value(vm, make_string("")); break; }
|
if (!f) {
|
||||||
if (fseek(f, 0, SEEK_END) != 0) { fclose(f); free_value(path); push_value(vm, make_string("")); break; }
|
free_value(path);
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fseek(f, 0, SEEK_END) != 0) {
|
||||||
|
fclose(f);
|
||||||
|
free_value(path);
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
long sz = ftell(f);
|
long sz = ftell(f);
|
||||||
if (sz < 0) { fclose(f); free_value(path); push_value(vm, make_string("")); break; }
|
if (sz < 0) {
|
||||||
|
fclose(f);
|
||||||
|
free_value(path);
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
rewind(f);
|
rewind(f);
|
||||||
char *buf = (char*)malloc((size_t)sz + 1);
|
char *buf = (char *)malloc((size_t)sz + 1);
|
||||||
size_t n = buf ? fread(buf, 1, (size_t)sz, f) : 0;
|
size_t n = buf ? fread(buf, 1, (size_t)sz, f) : 0;
|
||||||
fclose(f);
|
fclose(f);
|
||||||
if (!buf) { free_value(path); push_value(vm, make_string("")); break; }
|
if (!buf) {
|
||||||
|
free_value(path);
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
buf[n] = '\0';
|
buf[n] = '\0';
|
||||||
Value out = make_string(buf);
|
Value out = make_string(buf);
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file write_file.c
|
* @file write_file.c
|
||||||
* @brief Implements the OP_WRITE_FILE opcode for writing to a file in the VM.
|
* @brief Implements the OP_WRITE_FILE opcode for writing to a file in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_WRITE_FILE instruction, which writes data to a file.
|
* This file handles the OP_WRITE_FILE instruction, which writes data to a file.
|
||||||
|
|
@ -34,7 +34,10 @@
|
||||||
case OP_WRITE_FILE: {
|
case OP_WRITE_FILE: {
|
||||||
Value data = pop_value(vm);
|
Value data = pop_value(vm);
|
||||||
Value path = pop_value(vm);
|
Value path = pop_value(vm);
|
||||||
if (path.type != VAL_STRING || data.type != VAL_STRING) { fprintf(stderr, "WRITE_FILE expects (string, string)\n"); exit(1); }
|
if (path.type != VAL_STRING || data.type != VAL_STRING) {
|
||||||
|
fprintf(stderr, "WRITE_FILE expects (string, string)\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
const char *p = path.s ? path.s : "";
|
const char *p = path.s ? path.s : "";
|
||||||
FILE *f = fopen(p, "wb");
|
FILE *f = fopen(p, "wb");
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -15,15 +15,22 @@ case OP_JSON_FROM_FILE: {
|
||||||
Value vpath = pop_value(vm);
|
Value vpath = pop_value(vm);
|
||||||
char *path = value_to_string_alloc(&vpath);
|
char *path = value_to_string_alloc(&vpath);
|
||||||
free_value(vpath);
|
free_value(vpath);
|
||||||
if (!path) { push_value(vm, make_nil()); break; }
|
if (!path) {
|
||||||
|
push_value(vm, make_nil());
|
||||||
|
break;
|
||||||
|
}
|
||||||
json_object *root = json_object_from_file(path);
|
json_object *root = json_object_from_file(path);
|
||||||
free(path);
|
free(path);
|
||||||
if (!root) { push_value(vm, make_nil()); break; }
|
if (!root) {
|
||||||
|
push_value(vm, make_nil());
|
||||||
|
break;
|
||||||
|
}
|
||||||
Value v = json_to_fun(root);
|
Value v = json_to_fun(root);
|
||||||
push_value(vm, v);
|
push_value(vm, v);
|
||||||
json_object_put(root);
|
json_object_put(root);
|
||||||
#else
|
#else
|
||||||
Value vpath = pop_value(vm); free_value(vpath);
|
Value vpath = pop_value(vm);
|
||||||
|
free_value(vpath);
|
||||||
push_value(vm, make_nil());
|
push_value(vm, make_nil());
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -15,7 +15,10 @@ case OP_JSON_PARSE: {
|
||||||
Value text = pop_value(vm);
|
Value text = pop_value(vm);
|
||||||
char *s = value_to_string_alloc(&text);
|
char *s = value_to_string_alloc(&text);
|
||||||
free_value(text);
|
free_value(text);
|
||||||
if (!s) { push_value(vm, make_nil()); break; }
|
if (!s) {
|
||||||
|
push_value(vm, make_nil());
|
||||||
|
break;
|
||||||
|
}
|
||||||
struct json_tokener *tok = json_tokener_new();
|
struct json_tokener *tok = json_tokener_new();
|
||||||
json_object *root = json_tokener_parse_ex(tok, s, (int)strlen(s));
|
json_object *root = json_tokener_parse_ex(tok, s, (int)strlen(s));
|
||||||
enum json_tokener_error jerr = json_tokener_get_error(tok);
|
enum json_tokener_error jerr = json_tokener_get_error(tok);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -24,8 +24,10 @@ case OP_JSON_STRINGIFY: {
|
||||||
free_value(any);
|
free_value(any);
|
||||||
#else
|
#else
|
||||||
/* Fallback: consume two args, push "null" */
|
/* Fallback: consume two args, push "null" */
|
||||||
Value vpretty = pop_value(vm); free_value(vpretty);
|
Value vpretty = pop_value(vm);
|
||||||
Value any = pop_value(vm); free_value(any);
|
free_value(vpretty);
|
||||||
|
Value any = pop_value(vm);
|
||||||
|
free_value(any);
|
||||||
push_value(vm, make_string("null"));
|
push_value(vm, make_string("null"));
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This file is part of the Fun programming language.
|
* This file is part of the Fun programming language.
|
||||||
* https://fun-lang.xyz/
|
* https://fun-lang.xyz/
|
||||||
*
|
*
|
||||||
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
* Copyright 2025 Johannes Findeisen <you@hanez.org>
|
||||||
|
|
@ -19,7 +19,11 @@ case OP_JSON_TO_FILE: {
|
||||||
int pretty = (vpretty.type == VAL_BOOL || vpretty.type == VAL_INT) ? (vpretty.i != 0) : 0;
|
int pretty = (vpretty.type == VAL_BOOL || vpretty.type == VAL_INT) ? (vpretty.i != 0) : 0;
|
||||||
free_value(vpretty);
|
free_value(vpretty);
|
||||||
free_value(vpath);
|
free_value(vpath);
|
||||||
if (!path) { free_value(any); push_value(vm, make_int(0)); break; }
|
if (!path) {
|
||||||
|
free_value(any);
|
||||||
|
push_value(vm, make_int(0));
|
||||||
|
break;
|
||||||
|
}
|
||||||
json_object *j = fun_to_json(&any);
|
json_object *j = fun_to_json(&any);
|
||||||
int flags = pretty ? JSON_C_TO_STRING_PRETTY : JSON_C_TO_STRING_PLAIN;
|
int flags = pretty ? JSON_C_TO_STRING_PRETTY : JSON_C_TO_STRING_PLAIN;
|
||||||
int rc = json_object_to_file_ext(path, j, flags);
|
int rc = json_object_to_file_ext(path, j, flags);
|
||||||
|
|
@ -28,9 +32,12 @@ case OP_JSON_TO_FILE: {
|
||||||
free_value(any);
|
free_value(any);
|
||||||
push_value(vm, make_int(rc == 0 ? 1 : 0));
|
push_value(vm, make_int(rc == 0 ? 1 : 0));
|
||||||
#else
|
#else
|
||||||
Value vpretty = pop_value(vm); free_value(vpretty);
|
Value vpretty = pop_value(vm);
|
||||||
Value any = pop_value(vm); free_value(any);
|
free_value(vpretty);
|
||||||
Value vpath = pop_value(vm); free_value(vpath);
|
Value any = pop_value(vm);
|
||||||
|
free_value(any);
|
||||||
|
Value vpath = pop_value(vm);
|
||||||
|
free_value(vpath);
|
||||||
push_value(vm, make_int(0));
|
push_value(vm, make_int(0));
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file len.c
|
* @file len.c
|
||||||
* @brief Implements the OP_LEN opcode for getting the length of arrays or strings in the VM.
|
* @brief Implements the OP_LEN opcode for getting the length of arrays or strings in the VM.
|
||||||
*
|
*
|
||||||
* This file handles the OP_LEN instruction, which retrieves the length of an array or string.
|
* This file handles the OP_LEN instruction, which retrieves the length of an array or string.
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LibreSSL MD5 builtin
|
* LibreSSL MD5 builtin
|
||||||
*/
|
*/
|
||||||
case OP_LIBRESSL_MD5: {
|
case OP_LIBRESSL_MD5: {
|
||||||
Value vdata = pop_value(vm);
|
Value vdata = pop_value(vm);
|
||||||
char *s = value_to_string_alloc(&vdata);
|
char *s = value_to_string_alloc(&vdata);
|
||||||
free_value(vdata);
|
free_value(vdata);
|
||||||
if (!s) { push_value(vm, make_string("")); break; }
|
if (!s) {
|
||||||
char *hex = fun_libressl_md5_hex((const unsigned char*)s, strlen(s));
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char *hex = fun_libressl_md5_hex((const unsigned char *)s, strlen(s));
|
||||||
free(s);
|
free(s);
|
||||||
if (!hex) { push_value(vm, make_string("")); break; }
|
if (!hex) {
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
Value out = make_string(hex);
|
Value out = make_string(hex);
|
||||||
free(hex);
|
free(hex);
|
||||||
push_value(vm, out);
|
push_value(vm, out);
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,16 @@ case OP_LIBRESSL_RIPEMD160: {
|
||||||
Value vdata = pop_value(vm);
|
Value vdata = pop_value(vm);
|
||||||
char *s = value_to_string_alloc(&vdata);
|
char *s = value_to_string_alloc(&vdata);
|
||||||
free_value(vdata);
|
free_value(vdata);
|
||||||
if (!s) { push_value(vm, make_string("")); break; }
|
if (!s) {
|
||||||
char *hex = fun_libressl_ripemd160_hex((const unsigned char*)s, strlen(s));
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char *hex = fun_libressl_ripemd160_hex((const unsigned char *)s, strlen(s));
|
||||||
free(s);
|
free(s);
|
||||||
if (!hex) { push_value(vm, make_string("")); break; }
|
if (!hex) {
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
Value out = make_string(hex);
|
Value out = make_string(hex);
|
||||||
free(hex);
|
free(hex);
|
||||||
push_value(vm, out);
|
push_value(vm, out);
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,16 @@ case OP_LIBRESSL_SHA256: {
|
||||||
Value vdata = pop_value(vm);
|
Value vdata = pop_value(vm);
|
||||||
char *s = value_to_string_alloc(&vdata);
|
char *s = value_to_string_alloc(&vdata);
|
||||||
free_value(vdata);
|
free_value(vdata);
|
||||||
if (!s) { push_value(vm, make_string("")); break; }
|
if (!s) {
|
||||||
char *hex = fun_libressl_sha256_hex((const unsigned char*)s, strlen(s));
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char *hex = fun_libressl_sha256_hex((const unsigned char *)s, strlen(s));
|
||||||
free(s);
|
free(s);
|
||||||
if (!hex) { push_value(vm, make_string("")); break; }
|
if (!hex) {
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
Value out = make_string(hex);
|
Value out = make_string(hex);
|
||||||
free(hex);
|
free(hex);
|
||||||
push_value(vm, out);
|
push_value(vm, out);
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,16 @@ case OP_LIBRESSL_SHA512: {
|
||||||
Value vdata = pop_value(vm);
|
Value vdata = pop_value(vm);
|
||||||
char *s = value_to_string_alloc(&vdata);
|
char *s = value_to_string_alloc(&vdata);
|
||||||
free_value(vdata);
|
free_value(vdata);
|
||||||
if (!s) { push_value(vm, make_string("")); break; }
|
if (!s) {
|
||||||
char *hex = fun_libressl_sha512_hex((const unsigned char*)s, strlen(s));
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
char *hex = fun_libressl_sha512_hex((const unsigned char *)s, strlen(s));
|
||||||
free(s);
|
free(s);
|
||||||
if (!hex) { push_value(vm, make_string("")); break; }
|
if (!hex) {
|
||||||
|
push_value(vm, make_string(""));
|
||||||
|
break;
|
||||||
|
}
|
||||||
Value out = make_string(hex);
|
Value out = make_string(hex);
|
||||||
free(hex);
|
free(hex);
|
||||||
push_value(vm, out);
|
push_value(vm, out);
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue